--- /dev/null
+/** @file\r
+Floppy Peim to support Recovery function from Floppy device.\r
+\r
+Copyright (c) 2006 - 2010, Intel Corporation. All rights reserved.<BR>\r
+ \r
+This program and the accompanying materials\r
+are licensed and made available under the terms and conditions\r
+of the BSD License which accompanies this distribution. The\r
+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
+\r
+**/\r
+\r
+\r
+#include "FloppyPeim.h"\r
+\r
+\r
+PEI_DMA_TABLE mRegisterTable[] = {\r
+ //\r
+ // DMA2: Clear Byte Ptr, Enable\r
+ //\r
+ {\r
+ R_8237_DMA_CBPR_CH4_7,\r
+ 0\r
+ },\r
+ {\r
+ R_8237_DMA_COMMAND_CH4_7,\r
+ 0\r
+ },\r
+ //\r
+ // DMA1: Clear Byte Ptr, Enable\r
+ //\r
+ {\r
+ R_8237_DMA_CBPR_CH0_3,\r
+ 0\r
+ },\r
+ {\r
+ R_8237_DMA_COMMAND_CH0_3,\r
+ 0\r
+ },\r
+ //\r
+ // Configure Channel 4 for Cascade Mode\r
+ // Clear DMA Request and enable DREQ\r
+ //\r
+ {\r
+ R_8237_DMA_CHMODE_CH4_7,\r
+ V_8237_DMA_CHMODE_CASCADE | 0\r
+ },\r
+ {\r
+ R_8237_DMA_STA_CH4_7,\r
+ 0\r
+ },\r
+ {\r
+ R_8237_DMA_WRSMSK_CH4_7,\r
+ 0\r
+ },\r
+ //\r
+ // Configure DMA1 (Channels 0-3) for Single Mode\r
+ // Clear DMA Request and enable DREQ\r
+ //\r
+ {\r
+ R_8237_DMA_CHMODE_CH0_3,\r
+ V_8237_DMA_CHMODE_SINGLE | 0\r
+ },\r
+ {\r
+ R_8237_DMA_STA_CH0_3,\r
+ 0\r
+ },\r
+ {\r
+ R_8237_DMA_WRSMSK_CH0_3,\r
+ 0\r
+ },\r
+ {\r
+ R_8237_DMA_CHMODE_CH0_3,\r
+ V_8237_DMA_CHMODE_SINGLE | 1\r
+ },\r
+ {\r
+ R_8237_DMA_STA_CH0_3,\r
+ 1\r
+ },\r
+ {\r
+ R_8237_DMA_WRSMSK_CH0_3,\r
+ 1\r
+ },\r
+ {\r
+ R_8237_DMA_CHMODE_CH0_3,\r
+ V_8237_DMA_CHMODE_SINGLE | 2\r
+ },\r
+ {\r
+ R_8237_DMA_STA_CH0_3,\r
+ 2\r
+ },\r
+ {\r
+ R_8237_DMA_WRSMSK_CH0_3,\r
+ 2\r
+ },\r
+ {\r
+ R_8237_DMA_CHMODE_CH0_3,\r
+ V_8237_DMA_CHMODE_SINGLE | 3\r
+ },\r
+ {\r
+ R_8237_DMA_STA_CH0_3,\r
+ 3\r
+ },\r
+ {\r
+ R_8237_DMA_WRSMSK_CH0_3,\r
+ 3\r
+ },\r
+ //\r
+ // Configure DMA2 (Channels 5-7) for Single Mode\r
+ // Clear DMA Request and enable DREQ\r
+ //\r
+ {\r
+ R_8237_DMA_CHMODE_CH4_7,\r
+ V_8237_DMA_CHMODE_SINGLE | 1\r
+ },\r
+ {\r
+ R_8237_DMA_STA_CH4_7,\r
+ 1\r
+ },\r
+ {\r
+ R_8237_DMA_WRSMSK_CH4_7,\r
+ 1\r
+ },\r
+ {\r
+ R_8237_DMA_CHMODE_CH4_7,\r
+ V_8237_DMA_CHMODE_SINGLE | 2\r
+ },\r
+ {\r
+ R_8237_DMA_STA_CH4_7,\r
+ 2\r
+ },\r
+ {\r
+ R_8237_DMA_WRSMSK_CH4_7,\r
+ 2\r
+ },\r
+ {\r
+ R_8237_DMA_CHMODE_CH4_7,\r
+ V_8237_DMA_CHMODE_SINGLE | 3\r
+ },\r
+ {\r
+ R_8237_DMA_STA_CH4_7,\r
+ 3\r
+ },\r
+ {\r
+ R_8237_DMA_WRSMSK_CH4_7,\r
+ 3\r
+ }\r
+};\r
+\r
+//\r
+// Table of diskette parameters of various diskette types \r
+//\r
+DISKET_PARA_TABLE DiskPara[9] = {\r
+ {\r
+ 0x09,\r
+ 0x50,\r
+ 0xff,\r
+ 0x2,\r
+ 0x27,\r
+ 0x4,\r
+ 0x25,\r
+ 0x14,\r
+ 0x80\r
+ },\r
+ {\r
+ 0x09,\r
+ 0x2a,\r
+ 0xff,\r
+ 0x2,\r
+ 0x27,\r
+ 0x4,\r
+ 0x25,\r
+ 0x0f,\r
+ 0x40\r
+ },\r
+ {\r
+ 0x0f,\r
+ 0x54,\r
+ 0xff,\r
+ 0x2,\r
+ 0x4f,\r
+ 0x4,\r
+ 0x25,\r
+ 0x0f,\r
+ 0x0\r
+ },\r
+ {\r
+ 0x09,\r
+ 0x50,\r
+ 0xff,\r
+ 0x2,\r
+ 0x4f,\r
+ 0x4,\r
+ 0x25,\r
+ 0x0f,\r
+ 0x80\r
+ },\r
+ {\r
+ 0x09,\r
+ 0x2a,\r
+ 0xff,\r
+ 0x2,\r
+ 0x4f,\r
+ 0x4,\r
+ 0x25,\r
+ 0x0f,\r
+ 0x80\r
+ },\r
+ {\r
+ 0x12,\r
+ 0x1b,\r
+ 0xff,\r
+ 0x2,\r
+ 0x4f,\r
+ 0x4,\r
+ 0x25,\r
+ 0x0f,\r
+ 0x0\r
+ },\r
+ {\r
+ 0x09,\r
+ 0x2a,\r
+ 0xff,\r
+ 0x2,\r
+ 0x4f,\r
+ 0x4,\r
+ 0x25,\r
+ 0x0f,\r
+ 0x80\r
+ },\r
+ {\r
+ 0x12,\r
+ 0x1b,\r
+ 0xff,\r
+ 0x2,\r
+ 0x4f,\r
+ 0x4,\r
+ 0x25,\r
+ 0x0f,\r
+ 0x0\r
+ },\r
+ {\r
+ 0x24,\r
+ 0x1b,\r
+ 0xff,\r
+ 0x2,\r
+ 0x4f,\r
+ 0x4,\r
+ 0x25,\r
+ 0x0f,\r
+ 0xc0\r
+ }\r
+};\r
+\r
+//\r
+// Byte per sector corresponding to various device types.\r
+//\r
+UINTN BytePerSector[6] = { 0, 256, 512, 1024, 2048, 4096 };\r
+\r
+FDC_BLK_IO_DEV mBlockIoDevTemplate = {\r
+ FDC_BLK_IO_DEV_SIGNATURE,\r
+ {\r
+ FdcGetNumberOfBlockDevices,\r
+ FdcGetBlockDeviceMediaInfo,\r
+ FdcReadBlocks,\r
+ },\r
+ {\r
+ (EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST),\r
+ &gEfiPeiVirtualBlockIoPpiGuid,\r
+ NULL\r
+ },\r
+ 0,\r
+ {{0}}\r
+};\r
+\r
+/**\r
+ Wait and check if bits for DIO and RQM of FDC Main Status Register\r
+ indicates FDC is ready for read or write.\r
+\r
+ Before writing to FDC or reading from FDC, the Host must examine\r
+ the bit7(RQM) and bit6(DIO) of the Main Status Register.\r
+ That is to say:\r
+ Command bytes can not be written to Data Register unless RQM is 1 and DIO is 0.\r
+ Result bytes can not be read from Data Register unless RQM is 1 and DIO is 1.\r
+\r
+ @param FdcBlkIoDev Instance of FDC_BLK_IO_DEV.\r
+ @param DataIn Indicates data input or output.\r
+ TRUE means input.\r
+ FALSE means output.\r
+ @param TimeoutInMseconds Timeout value to wait.\r
+ \r
+ @retval EFI_SUCCESS FDC is ready.\r
+ @retval EFI_NOT_READY FDC is not ready within the specified time period.\r
+\r
+**/\r
+EFI_STATUS\r
+FdcDRQReady (\r
+ IN FDC_BLK_IO_DEV *FdcBlkIoDev,\r
+ IN BOOLEAN DataIn,\r
+ IN UINTN TimeoutInMseconds\r
+ )\r
+{\r
+ UINTN Delay;\r
+ UINT8 StatusRegister;\r
+ UINT8 BitInOut;\r
+\r
+ //\r
+ // Check bit6 of Main Status Register.\r
+ //\r
+ BitInOut = 0;\r
+ if (DataIn) {\r
+ BitInOut = BIT6;\r
+ }\r
+\r
+ Delay = ((TimeoutInMseconds * STALL_1_MSECOND) / FDC_CHECK_INTERVAL) + 1;\r
+ do {\r
+ StatusRegister = IoRead8 ((UINT16) (PcdGet16 (PcdFdcBaseAddress) + FDC_REGISTER_MSR));\r
+ if ((StatusRegister & MSR_RQM) == MSR_RQM && (StatusRegister & MSR_DIO) == BitInOut) {\r
+ //\r
+ // FDC is ready\r
+ //\r
+ break;\r
+ }\r
+\r
+ MicroSecondDelay (FDC_SHORT_DELAY);\r
+ } while (--Delay > 0);\r
+\r
+ if (Delay == 0) {\r
+ //\r
+ // FDC is not ready within the specified time period\r
+ //\r
+ return EFI_NOT_READY;\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Read a byte from FDC data register.\r
+\r
+ @param FdcBlkIoDev Instance of FDC_BLK_IO_DEV.\r
+ @param Pointer Pointer to buffer to hold data read from FDC.\r
+\r
+ @retval EFI_SUCCESS Byte successfully read.\r
+ @retval EFI_DEVICE_ERROR FDC is not ready.\r
+\r
+**/\r
+EFI_STATUS\r
+DataInByte (\r
+ IN FDC_BLK_IO_DEV *FdcBlkIoDev,\r
+ OUT UINT8 *Pointer\r
+ )\r
+{\r
+ UINT8 Data;\r
+\r
+ //\r
+ // Wait for 1ms and detect the FDC is ready to be read\r
+ //\r
+ if (FdcDRQReady (FdcBlkIoDev, TRUE, 1) != EFI_SUCCESS) {\r
+ //\r
+ // FDC is not ready.\r
+ //\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+\r
+ Data = IoRead8 ((UINT16) (PcdGet16 (PcdFdcBaseAddress) + FDC_REGISTER_DTR));\r
+ MicroSecondDelay (FDC_SHORT_DELAY);\r
+ *Pointer = Data;\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Write a byte to FDC data register.\r
+\r
+ @param FdcBlkIoDev Instance of FDC_BLK_IO_DEV.\r
+ @param Pointer Pointer to data to write.\r
+\r
+ @retval EFI_SUCCESS Byte successfully written.\r
+ @retval EFI_DEVICE_ERROR FDC is not ready.\r
+\r
+**/\r
+EFI_STATUS\r
+DataOutByte (\r
+ IN FDC_BLK_IO_DEV *FdcBlkIoDev,\r
+ IN UINT8 *Pointer\r
+ )\r
+{\r
+ UINT8 Data;\r
+\r
+ //\r
+ // Wait for 1ms and detect the FDC is ready to be written\r
+ //\r
+ if (FdcDRQReady (FdcBlkIoDev, FALSE, 1) != EFI_SUCCESS) {\r
+ //\r
+ // FDC is not ready.\r
+ //\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+\r
+ Data = *Pointer;\r
+ IoWrite8 ((UINT16) (PcdGet16 (PcdFdcBaseAddress) + FDC_REGISTER_DTR), Data);\r
+ MicroSecondDelay (FDC_SHORT_DELAY);\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Get Sts0 and Pcn status from FDC\r
+\r
+ @param FdcBlkIoDev Instance of FDC_BLK_IO_DEV\r
+ @param Sts0 Value of Sts0\r
+ @param Pcn Value of Pcn\r
+\r
+ @retval EFI_SUCCESS Successfully retrieved status value of Sts0 and Pcn.\r
+ @retval EFI_DEVICE_ERROR Fail to send SENSE_INT_STATUS_CMD.\r
+ @retval EFI_DEVICE_ERROR Fail to read Sts0.\r
+ @retval EFI_DEVICE_ERROR Fail to read Pcn.\r
+\r
+**/\r
+EFI_STATUS\r
+SenseIntStatus (\r
+ IN FDC_BLK_IO_DEV *FdcBlkIoDev,\r
+ OUT UINT8 *Sts0,\r
+ OUT UINT8 *Pcn\r
+ )\r
+{\r
+ UINT8 Command;\r
+\r
+ Command = SENSE_INT_STATUS_CMD;\r
+\r
+ if (DataOutByte (FdcBlkIoDev, &Command) != EFI_SUCCESS) {\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+\r
+ if (DataInByte (FdcBlkIoDev, Sts0) != EFI_SUCCESS) {\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+\r
+ if (DataInByte (FdcBlkIoDev, Pcn) != EFI_SUCCESS) {\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Issue Specify command.\r
+\r
+ @param FdcBlkIoDev Instance of FDC_BLK_IO_DEV.\r
+\r
+ @retval EFI_SUCCESS Specify command successfully issued.\r
+ @retval EFI_DEVICE_ERROR FDC device has errors.\r
+\r
+**/\r
+EFI_STATUS\r
+Specify (\r
+ IN FDC_BLK_IO_DEV *FdcBlkIoDev\r
+ )\r
+{\r
+ FDC_SPECIFY_CMD Command;\r
+ UINTN Index;\r
+ UINT8 *Pointer;\r
+\r
+ ZeroMem (&Command, sizeof (FDC_SPECIFY_CMD));\r
+ Command.CommandCode = SPECIFY_CMD;\r
+ //\r
+ // set SRT, HUT\r
+ //\r
+ Command.SrtHut = 0xdf;\r
+ //\r
+ // 0xdf;\r
+ // set HLT and DMA\r
+ //\r
+ Command.HltNd = 0x02;\r
+\r
+ Pointer = (UINT8 *) (&Command);\r
+ for (Index = 0; Index < sizeof (FDC_SPECIFY_CMD); Index++) {\r
+ if (DataOutByte (FdcBlkIoDev, Pointer++) != EFI_SUCCESS) {\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Wait until busy bit is cleared.\r
+\r
+ @param FdcBlkIoDev Instance of FDC_BLK_IO_DEV.\r
+ @param DevPos Position of FDC (Driver A or B)\r
+ @param TimeoutInMseconds Timeout value to wait.\r
+\r
+ @retval EFI_SUCCESS Busy bit has been cleared before timeout.\r
+ @retval EFI_TIMEOUT Time goes out before busy bit is cleared.\r
+\r
+**/\r
+EFI_STATUS\r
+FdcWaitForBSYClear (\r
+ IN FDC_BLK_IO_DEV *FdcBlkIoDev,\r
+ IN UINT8 DevPos,\r
+ IN UINTN TimeoutInMseconds\r
+ )\r
+{\r
+ UINTN Delay;\r
+ UINT8 StatusRegister;\r
+ UINT8 Mask;\r
+\r
+ //\r
+ // How to determine drive and command are busy or not: by the bits of Main Status Register\r
+ // bit0: Drive 0 busy (drive A)\r
+ // bit1: Drive 1 busy (drive B)\r
+ // bit4: Command busy\r
+ //\r
+ // set mask: for drive A set bit0 & bit4; for drive B set bit1 & bit4\r
+ //\r
+ Mask = (UINT8) ((DevPos == 0 ? MSR_DAB : MSR_DBB) | MSR_CB);\r
+\r
+ Delay = ((TimeoutInMseconds * STALL_1_MSECOND) / FDC_CHECK_INTERVAL) + 1;\r
+\r
+ do {\r
+ StatusRegister = IoRead8 ((UINT16) (PcdGet16 (PcdFdcBaseAddress) + FDC_REGISTER_MSR));\r
+\r
+ if ((StatusRegister & Mask) == 0x00) {\r
+ //\r
+ // not busy\r
+ //\r
+ break;\r
+ }\r
+\r
+ MicroSecondDelay (FDC_SHORT_DELAY);\r
+ } while (--Delay > 0);\r
+\r
+ if (Delay == 0) {\r
+ return EFI_TIMEOUT;\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Reset FDC device.\r
+\r
+ @param FdcBlkIoDev Instance of FDC_BLK_IO_DEV\r
+ @param DevPos Index of FDC device.\r
+\r
+ @retval EFI_SUCCESS FDC device successfully reset.\r
+ @retval EFI_DEVICE_ERROR Fail to reset FDC device.\r
+\r
+**/\r
+EFI_STATUS\r
+FdcReset (\r
+ IN FDC_BLK_IO_DEV *FdcBlkIoDev,\r
+ IN UINT8 DevPos\r
+ )\r
+{\r
+ UINT8 Data;\r
+ UINT8 Sts0;\r
+ UINT8 Pcn;\r
+ UINTN Index;\r
+\r
+ //\r
+ // Reset specified Floppy Logic Drive according to Fdd -> Disk\r
+ // Set Digital Output Register(DOR) to do reset work\r
+ // bit0 & bit1 of DOR : Drive Select\r
+ // bit2 : Reset bit\r
+ // bit3 : DMA and Int bit\r
+ // Reset : A "0" written to bit2 resets the FDC, this reset will remain active until\r
+ // a "1" is written to this bit.\r
+ // Reset step 1:\r
+ // use bit0 & bit1 to select the logic drive\r
+ // write "0" to bit2\r
+ //\r
+ Data = 0x0;\r
+ Data = (UINT8) (Data | (SELECT_DRV & DevPos));\r
+ IoWrite8 ((UINT16) (PcdGet16 (PcdFdcBaseAddress) + FDC_REGISTER_DOR), Data);\r
+\r
+ //\r
+ // Wait some time, at least 120us.\r
+ //\r
+ MicroSecondDelay (FDC_RESET_DELAY);\r
+ //\r
+ // Reset step 2:\r
+ // write "1" to bit2\r
+ // write "1" to bit3 : enable DMA\r
+ //\r
+ Data |= 0x0C;\r
+ IoWrite8 ((UINT16) (PcdGet16 (PcdFdcBaseAddress) + FDC_REGISTER_DOR), Data);\r
+\r
+ MicroSecondDelay (FDC_RESET_DELAY);\r
+\r
+ //\r
+ // Wait until specified floppy logic drive is not busy\r
+ //\r
+ if (FdcWaitForBSYClear (FdcBlkIoDev, DevPos, 1) != EFI_SUCCESS) {\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+ //\r
+ // Set the Transfer Data Rate\r
+ //\r
+ IoWrite8 ((UINT16) (PcdGet16 (PcdFdcBaseAddress) + FDC_REGISTER_CCR), 0x0);\r
+\r
+ MicroSecondDelay (FDC_MEDIUM_DELAY);\r
+\r
+ //\r
+ // Issue Sense interrupt command for each drive (totally 4 drives)\r
+ //\r
+ for (Index = 0; Index < 4; Index++) {\r
+ if (SenseIntStatus (FdcBlkIoDev, &Sts0, &Pcn) != EFI_SUCCESS) {\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+ }\r
+ //\r
+ // Issue Specify command\r
+ //\r
+ if (Specify (FdcBlkIoDev) != EFI_SUCCESS) {\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Turn on the motor of floppy drive.\r
+\r
+ @param FdcBlkIoDev Instance of FDC_BLK_IO_DEV.\r
+ @param Info Information of floppy device.\r
+\r
+ @retval EFI_SUCCESS Motor is successfully turned on.\r
+ @retval EFI_SUCCESS Motor is already on.\r
+ @retval EFI_DEVICE_ERROR Busy bit of FDC cannot be cleared.\r
+\r
+**/\r
+EFI_STATUS\r
+MotorOn (\r
+ IN FDC_BLK_IO_DEV *FdcBlkIoDev,\r
+ IN OUT PEI_FLOPPY_DEVICE_INFO *Info\r
+ )\r
+{\r
+ UINT8 Data;\r
+ UINT8 DevPos;\r
+\r
+ //\r
+ // Control of the floppy drive motors is a big pain. If motor is off, you have to turn it\r
+ // on first. But you can not leave the motor on all the time, since that would wear out the\r
+ // disk. On the other hand, if you turn the motor off after each operation, the system performance\r
+ // will be awful. The compromise used in this driver is to leave the motor on for 2 seconds after\r
+ // each operation. If a new operation is started in that interval(2s), the motor need not be\r
+ // turned on again. If no new operation is started, a timer goes off and the motor is turned off.\r
+ //\r
+ DevPos = Info->DevPos;\r
+\r
+ //\r
+ // If the Motor is already on, just return EFI_SUCCESS.\r
+ //\r
+ if (Info->MotorOn) {\r
+ return EFI_SUCCESS;\r
+ }\r
+ //\r
+ // The drive's motor is off, so need turn it on.\r
+ // First check if command and drive are busy or not.\r
+ //\r
+ if (FdcWaitForBSYClear (FdcBlkIoDev, DevPos, 1) != EFI_SUCCESS) {\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+ //\r
+ // for drive A: 1CH, drive B: 2DH\r
+ //\r
+ Data = 0x0C;\r
+ Data = (UINT8) (Data | (SELECT_DRV & DevPos));\r
+ if (DevPos == 0) {\r
+ Data |= DRVA_MOTOR_ON;\r
+ } else {\r
+ Data |= DRVB_MOTOR_ON;\r
+ }\r
+\r
+ Info->MotorOn = FALSE;\r
+\r
+ //\r
+ // Turn on the motor and wait for some time to ensure it takes effect.\r
+ //\r
+ IoWrite8 ((UINT16) (PcdGet16 (PcdFdcBaseAddress) + FDC_REGISTER_DOR), Data);\r
+ MicroSecondDelay (FDC_LONG_DELAY);\r
+\r
+ Info->MotorOn = TRUE;\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Turn off the motor of floppy drive.\r
+\r
+ @param FdcBlkIoDev Instance of FDC_BLK_IO_DEV.\r
+ @param Info Information of floppy device.\r
+\r
+**/\r
+VOID\r
+MotorOff (\r
+ IN FDC_BLK_IO_DEV *FdcBlkIoDev,\r
+ IN OUT PEI_FLOPPY_DEVICE_INFO *Info\r
+ )\r
+{\r
+ UINT8 Data;\r
+ UINT8 DevPos;\r
+\r
+ DevPos = Info->DevPos;\r
+\r
+ if (!Info->MotorOn) {\r
+ return;\r
+ }\r
+ //\r
+ // The motor is on, so need motor off\r
+ //\r
+ Data = 0x0C;\r
+ Data = (UINT8) (Data | (SELECT_DRV & DevPos));\r
+\r
+ IoWrite8 ((UINT16) (PcdGet16 (PcdFdcBaseAddress) + FDC_REGISTER_DOR), Data);\r
+ MicroSecondDelay (FDC_SHORT_DELAY);\r
+\r
+ Info->MotorOn = FALSE;\r
+}\r
+\r
+/**\r
+ Recalibrate the FDC device.\r
+\r
+ @param FdcBlkIoDev Instance of FDC_BLK_IO_DEV.\r
+ @param Info Information of floppy device.\r
+\r
+ @retval EFI_SUCCESS FDC successfully recalibrated.\r
+ @retval EFI_DEVICE_ERROR Fail to send RECALIBRATE_CMD.\r
+ @retval EFI_DEVICE_ERROR Fail to get status value of Sts0 and Pcn.\r
+ @retval EFI_DEVICE_ERROR Fail to recalibrate FDC device.\r
+\r
+**/\r
+EFI_STATUS\r
+Recalibrate (\r
+ IN FDC_BLK_IO_DEV *FdcBlkIoDev,\r
+ IN OUT PEI_FLOPPY_DEVICE_INFO *Info\r
+ )\r
+{\r
+ FDC_COMMAND_PACKET2 Command;\r
+ UINTN Index;\r
+ UINT8 Sts0;\r
+ UINT8 Pcn;\r
+ UINT8 *Pointer;\r
+ UINT8 Count;\r
+ UINT8 DevPos;\r
+\r
+ DevPos = Info->DevPos;\r
+\r
+ //\r
+ // We would try twice.\r
+ //\r
+ Count = 2;\r
+ while (Count > 0) {\r
+ ZeroMem (&Command, sizeof (FDC_COMMAND_PACKET2));\r
+ Command.CommandCode = RECALIBRATE_CMD;\r
+ //\r
+ // drive select\r
+ //\r
+ if (DevPos == 0) {\r
+ Command.DiskHeadSel = 0;\r
+ } else {\r
+ Command.DiskHeadSel = 1;\r
+ }\r
+\r
+ Pointer = (UINT8 *) (&Command);\r
+ for (Index = 0; Index < sizeof (FDC_COMMAND_PACKET2); Index++) {\r
+ if (DataOutByte (FdcBlkIoDev, Pointer++) != EFI_SUCCESS) {\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+ }\r
+\r
+ MicroSecondDelay (FDC_RECALIBRATE_DELAY);\r
+\r
+ if (SenseIntStatus (FdcBlkIoDev, &Sts0, &Pcn) != EFI_SUCCESS) {\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+\r
+ if ((Sts0 & 0xf0) == BIT5 && Pcn == 0) {\r
+ //\r
+ // Recalibration is successful. \r
+ //\r
+ Info->Pcn = 0;\r
+ Info->NeedRecalibrate = FALSE;\r
+\r
+ return EFI_SUCCESS;\r
+ } else {\r
+ //\r
+ // Recalibration is not successful. Try again.\r
+ // If trial is used out, return EFI_DEVICE_ERROR.\r
+ //\r
+ Count--;\r
+ if (Count == 0) {\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+ }\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Seek for the cylinder according to given LBA.\r
+\r
+ @param FdcBlkIoDev Instance of FDC_BLK_IO_DEV.\r
+ @param Info Information of floppy device.\r
+ @param Lba LBA for which to seek for cylinder.\r
+\r
+ @retval EFI_SUCCESS Successfully moved to the destination cylinder.\r
+ @retval EFI_SUCCESS Destination cylinder is just the present cylinder.\r
+ @retval EFI_DEVICE_ERROR Fail to move to the destination cylinder.\r
+\r
+**/\r
+EFI_STATUS\r
+Seek (\r
+ IN FDC_BLK_IO_DEV *FdcBlkIoDev,\r
+ IN OUT PEI_FLOPPY_DEVICE_INFO *Info,\r
+ IN EFI_PEI_LBA Lba\r
+ )\r
+{\r
+ FDC_SEEK_CMD Command;\r
+ DISKET_PARA_TABLE *Para;\r
+ UINT8 EndOfTrack;\r
+ UINT8 Head;\r
+ UINT8 Cylinder;\r
+ UINT8 Sts0;\r
+ UINT8 *Pointer;\r
+ UINT8 Pcn;\r
+ UINTN Index;\r
+ UINT8 Gap;\r
+ UINT8 DevPos;\r
+\r
+ DevPos = Info->DevPos;\r
+ if (Info->NeedRecalibrate) {\r
+ if (Recalibrate (FdcBlkIoDev, Info) != EFI_SUCCESS) {\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+ //\r
+ // Recalibrate Success\r
+ //\r
+ Info->NeedRecalibrate = FALSE;\r
+ }\r
+\r
+ //\r
+ // Get the base of disk parameter information corresponding to its type.\r
+ //\r
+ Para = (DISKET_PARA_TABLE *) ((UINT8 *) DiskPara + sizeof (DISKET_PARA_TABLE) * Info->Type);\r
+ EndOfTrack = Para->EndOfTrack;\r
+ //\r
+ // Calculate cylinder based on Lba and EOT\r
+ //\r
+ Cylinder = (UINT8) ((UINTN) Lba / EndOfTrack / 2);\r
+\r
+ //\r
+ // If the dest cylinder is the present cylinder, unnecessary to do the seek operation\r
+ //\r
+ if (Info->Pcn == Cylinder) {\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ //\r
+ // Calculate the head : 0 or 1\r
+ //\r
+ Head = (UINT8) ((UINTN) Lba / EndOfTrack % 2);\r
+\r
+ ZeroMem (&Command, sizeof (FDC_SEEK_CMD));\r
+ Command.CommandCode = SEEK_CMD;\r
+ if (DevPos == 0) {\r
+ Command.DiskHeadSel = 0;\r
+ } else {\r
+ Command.DiskHeadSel = 1;\r
+ }\r
+\r
+ //\r
+ // Send command to move to destination cylinder.\r
+ //\r
+ Command.DiskHeadSel = (UINT8) (Command.DiskHeadSel | (Head << 2));\r
+ Command.NewCylinder = Cylinder;\r
+\r
+ Pointer = (UINT8 *) (&Command);\r
+ for (Index = 0; Index < sizeof (FDC_SEEK_CMD); Index++) {\r
+ if (DataOutByte (FdcBlkIoDev, Pointer++) != EFI_SUCCESS) {\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+ }\r
+\r
+ MicroSecondDelay (FDC_SHORT_DELAY);\r
+\r
+ //\r
+ // Calculate waiting time, which is proportional to the gap between destination\r
+ // cylinder and present cylinder.\r
+ //\r
+ if (Info->Pcn > Cylinder) {\r
+ Gap = (UINT8) (Info->Pcn - Cylinder);\r
+ } else {\r
+ Gap = (UINT8) (Cylinder - Info->Pcn);\r
+ }\r
+\r
+ MicroSecondDelay ((Gap + 1) * FDC_LONG_DELAY);\r
+\r
+ //\r
+ // Confirm if the new cylinder is the destination and status is correct.\r
+ //\r
+ if (SenseIntStatus (FdcBlkIoDev, &Sts0, &Pcn) != EFI_SUCCESS) {\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+\r
+ if ((Sts0 & 0xf0) == BIT5) {\r
+ Info->Pcn = Command.NewCylinder;\r
+ Info->NeedRecalibrate = FALSE;\r
+ return EFI_SUCCESS;\r
+ } else {\r
+ Info->NeedRecalibrate = TRUE;\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+}\r
+\r
+/**\r
+ Check if diskette is changed.\r
+\r
+ @param FdcBlkIoDev Instance of FDC_BLK_IO_DEV\r
+ @param Info Information of floppy device.\r
+\r
+ @retval EFI_SUCCESS Diskette is not changed.\r
+ @retval EFI_MEDIA_CHANGED Diskette is changed.\r
+ @retval EFI_NO_MEDIA No diskette.\r
+ @retval EFI_DEVICE_ERROR Fail to do the seek or recalibrate operation.\r
+\r
+**/\r
+EFI_STATUS\r
+DisketChanged (\r
+ IN FDC_BLK_IO_DEV *FdcBlkIoDev,\r
+ IN OUT PEI_FLOPPY_DEVICE_INFO *Info\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ UINT8 Data;\r
+\r
+ //\r
+ // Check change line\r
+ //\r
+ Data = IoRead8 ((UINT16) (PcdGet16 (PcdFdcBaseAddress) + FDC_REGISTER_DIR));\r
+\r
+ MicroSecondDelay (FDC_SHORT_DELAY);\r
+\r
+ if ((Data & DIR_DCL) == DIR_DCL) {\r
+ if (Info->Pcn != 0) {\r
+ Status = Recalibrate (FdcBlkIoDev, Info);\r
+ } else {\r
+ Status = Seek (FdcBlkIoDev, Info, 0x30);\r
+ }\r
+\r
+ if (Status != EFI_SUCCESS) {\r
+ //\r
+ // Fail to do the seek or recalibrate operation\r
+ //\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+\r
+ Data = IoRead8 ((UINT16) (PcdGet16 (PcdFdcBaseAddress) + FDC_REGISTER_DIR));\r
+\r
+ MicroSecondDelay (FDC_SHORT_DELAY);\r
+\r
+ if ((Data & DIR_DCL) == DIR_DCL) {\r
+ return EFI_NO_MEDIA;\r
+ }\r
+\r
+ return EFI_MEDIA_CHANGED;\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Detects if FDC device exists.\r
+\r
+ @param FdcBlkIoDev Instance of FDC_BLK_IO_DEV\r
+ @param Info Information of floppy device.\r
+ @param MediaInfo Information of floppy media.\r
+\r
+ @retval TRUE FDC device exists and is working properly.\r
+ @retval FALSE FDC device does not exist or cannot work properly.\r
+\r
+**/\r
+BOOLEAN\r
+DiscoverFdcDevice (\r
+ IN FDC_BLK_IO_DEV *FdcBlkIoDev,\r
+ IN OUT PEI_FLOPPY_DEVICE_INFO *Info,\r
+ OUT EFI_PEI_BLOCK_IO_MEDIA *MediaInfo\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ DISKET_PARA_TABLE *Para;\r
+\r
+ Status = MotorOn (FdcBlkIoDev, Info);\r
+ if (Status != EFI_SUCCESS) {\r
+ return FALSE;\r
+ }\r
+\r
+ Status = Recalibrate (FdcBlkIoDev, Info);\r
+\r
+ if (Status != EFI_SUCCESS) {\r
+ MotorOff (FdcBlkIoDev, Info);\r
+ return FALSE;\r
+ }\r
+ //\r
+ // Set Media Parameter\r
+ //\r
+ MediaInfo->DeviceType = LegacyFloppy;\r
+ MediaInfo->MediaPresent = TRUE;\r
+\r
+ //\r
+ // Check Media\r
+ //\r
+ Status = DisketChanged (FdcBlkIoDev, Info);\r
+ switch (Status) {\r
+ case EFI_NO_MEDIA:\r
+ //\r
+ // No diskette in floppy.\r
+ //\r
+ MediaInfo->MediaPresent = FALSE;\r
+ break;\r
+\r
+ case EFI_MEDIA_CHANGED:\r
+ case EFI_SUCCESS:\r
+ //\r
+ // Diskette exists in floppy.\r
+ //\r
+ break;\r
+\r
+ default:\r
+ //\r
+ // EFI_DEVICE_ERROR\r
+ //\r
+ MotorOff (FdcBlkIoDev, Info);\r
+ return FALSE;\r
+ }\r
+\r
+ MotorOff (FdcBlkIoDev, Info);\r
+\r
+ //\r
+ // Get the base of disk parameter information corresponding to its type.\r
+ //\r
+ Para = (DISKET_PARA_TABLE *) ((UINT8 *) DiskPara + sizeof (DISKET_PARA_TABLE) * Info->Type);\r
+\r
+ MediaInfo->BlockSize = BytePerSector[Para->Number];\r
+ MediaInfo->LastBlock = Para->EndOfTrack * 2 * (Para->MaxTrackNum + 1) - 1;\r
+\r
+ return TRUE;\r
+}\r
+\r
+/**\r
+ Enumerate floppy device\r
+\r
+ @param FdcBlkIoDev Instance of floppy device controller\r
+\r
+ @return Number of FDC devices.\r
+\r
+**/\r
+UINT8\r
+FdcEnumeration (\r
+ IN FDC_BLK_IO_DEV *FdcBlkIoDev\r
+ )\r
+{\r
+ UINT8 DevPos;\r
+ UINT8 DevNo;\r
+ EFI_PEI_BLOCK_IO_MEDIA MediaInfo;\r
+ EFI_STATUS Status;\r
+\r
+ DevNo = 0;\r
+\r
+ //\r
+ // DevPos=0 means Drive A, 1 means Drive B.\r
+ //\r
+ for (DevPos = 0; DevPos < 2; DevPos++) {\r
+ //\r
+ // Detecting device presence\r
+ //\r
+ REPORT_STATUS_CODE (EFI_PROGRESS_CODE, EFI_PERIPHERAL_REMOVABLE_MEDIA + EFI_P_PC_PRESENCE_DETECT);\r
+\r
+ //\r
+ // Reset FDC\r
+ //\r
+ Status = FdcReset (FdcBlkIoDev, DevPos);\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ continue;\r
+ }\r
+\r
+ FdcBlkIoDev->DeviceInfo[DevPos].DevPos = DevPos;\r
+ FdcBlkIoDev->DeviceInfo[DevPos].Pcn = 0;\r
+ FdcBlkIoDev->DeviceInfo[DevPos].MotorOn = FALSE;\r
+ FdcBlkIoDev->DeviceInfo[DevPos].NeedRecalibrate = TRUE;\r
+ FdcBlkIoDev->DeviceInfo[DevPos].Type = FdcType1440K1440K;\r
+\r
+ //\r
+ // Discover FDC device\r
+ //\r
+ if (DiscoverFdcDevice (FdcBlkIoDev, &(FdcBlkIoDev->DeviceInfo[DevPos]), &MediaInfo)) {\r
+ FdcBlkIoDev->DeviceInfo[DevNo].DevPos = DevPos;\r
+\r
+ FdcBlkIoDev->DeviceInfo[DevNo].Pcn = FdcBlkIoDev->DeviceInfo[DevPos].Pcn;\r
+ FdcBlkIoDev->DeviceInfo[DevNo].MotorOn = FdcBlkIoDev->DeviceInfo[DevPos].MotorOn;\r
+ FdcBlkIoDev->DeviceInfo[DevNo].NeedRecalibrate = FdcBlkIoDev->DeviceInfo[DevPos].NeedRecalibrate;\r
+ FdcBlkIoDev->DeviceInfo[DevNo].Type = FdcBlkIoDev->DeviceInfo[DevPos].Type;\r
+\r
+ CopyMem (\r
+ &(FdcBlkIoDev->DeviceInfo[DevNo].MediaInfo),\r
+ &MediaInfo,\r
+ sizeof (EFI_PEI_BLOCK_IO_MEDIA)\r
+ );\r
+\r
+ DevNo++;\r
+ } else {\r
+ //\r
+ // Assume controller error\r
+ //\r
+ REPORT_STATUS_CODE (\r
+ EFI_ERROR_CODE | EFI_ERROR_MINOR,\r
+ EFI_PERIPHERAL_REMOVABLE_MEDIA + EFI_P_EC_CONTROLLER_ERROR\r
+ );\r
+ }\r
+ }\r
+\r
+ FdcBlkIoDev->DeviceCount = DevNo;\r
+ return DevNo;\r
+}\r
+\r
+/**\r
+ Checks result reflected by FDC_RESULT_PACKET.\r
+\r
+ @param Result FDC_RESULT_PACKET read from FDC after certain operation.\r
+ @param Info Information of floppy device.\r
+\r
+ @retval EFI_SUCCESS Result is healthy.\r
+ @retval EFI_DEVICE_ERROR Result is not healthy.\r
+\r
+**/\r
+EFI_STATUS\r
+CheckResult (\r
+ IN FDC_RESULT_PACKET *Result,\r
+ OUT PEI_FLOPPY_DEVICE_INFO *Info\r
+ )\r
+{\r
+ if ((Result->Status0 & STS0_IC) != IC_NT) {\r
+ if ((Result->Status0 & STS0_SE) == BIT5) {\r
+ //\r
+ // Seek error\r
+ //\r
+ Info->NeedRecalibrate = TRUE;\r
+ }\r
+\r
+ Info->NeedRecalibrate = TRUE;\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+ //\r
+ // Check Status Register1\r
+ //\r
+ if ((Result->Status1 & (STS1_EN | STS1_DE | STS1_OR | STS1_ND | STS1_NW | STS1_MA)) != 0) {\r
+ Info->NeedRecalibrate = TRUE;\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+ //\r
+ // Check Status Register2\r
+ //\r
+ if ((Result->Status2 & (STS2_CM | STS2_DD | STS2_WC | STS2_BC | STS2_MD)) != 0) {\r
+ Info->NeedRecalibrate = TRUE;\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Fill parameters for command packet.\r
+\r
+ @param Info Information of floppy device.\r
+ @param Lba Logical block address.\r
+ @param Command Command for which for fill parameters.\r
+\r
+**/\r
+VOID\r
+FillPara (\r
+ IN PEI_FLOPPY_DEVICE_INFO *Info,\r
+ IN EFI_PEI_LBA Lba,\r
+ OUT FDC_COMMAND_PACKET1 *Command\r
+ )\r
+{\r
+ DISKET_PARA_TABLE *Para;\r
+ UINT8 EndOfTrack;\r
+ UINT8 DevPos;\r
+\r
+ DevPos = Info->DevPos;\r
+\r
+ //\r
+ // Get the base of disk parameter information corresponding to its type.\r
+ //\r
+ Para = (DISKET_PARA_TABLE *) ((UINT8 *) DiskPara + sizeof (DISKET_PARA_TABLE) * Info->Type);\r
+\r
+ EndOfTrack = Para->EndOfTrack;\r
+\r
+ if (DevPos == 0) {\r
+ Command->DiskHeadSel = 0;\r
+ } else {\r
+ Command->DiskHeadSel = 1;\r
+ }\r
+\r
+ Command->Cylinder = (UINT8) ((UINTN) Lba / EndOfTrack / 2);\r
+ Command->Head = (UINT8) ((UINTN) Lba / EndOfTrack % 2);\r
+ Command->Sector = (UINT8) ((UINT8) ((UINTN) Lba % EndOfTrack) + 1);\r
+ Command->DiskHeadSel = (UINT8) (Command->DiskHeadSel | (Command->Head << 2));\r
+ Command->Number = Para->Number;\r
+ Command->EndOfTrack = Para->EndOfTrack;\r
+ Command->GapLength = Para->GapLength;\r
+ Command->DataLength = Para->DataLength;\r
+}\r
+\r
+/**\r
+ Setup specifed FDC device.\r
+\r
+ @param FdcBlkIoDev Instance of FDC_BLK_IO_DEV.\r
+ @param DevPos Index of FDC device.\r
+\r
+ @retval EFI_SUCCESS FDC device successfully set up.\r
+ @retval EFI_DEVICE_ERROR FDC device has errors.\r
+\r
+**/\r
+EFI_STATUS\r
+Setup (\r
+ IN FDC_BLK_IO_DEV *FdcBlkIoDev,\r
+ IN UINT8 DevPos\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+\r
+ IoWrite8 ((UINT16) (PcdGet16 (PcdFdcBaseAddress) + FDC_REGISTER_CCR), 0x0);\r
+\r
+ MicroSecondDelay (FDC_MEDIUM_DELAY);\r
+\r
+ Status = Specify (FdcBlkIoDev);\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Setup DMA channels to read data.\r
+\r
+ @param FdcBlkIoDev Instance of FDC_BLK_IO_DEV.\r
+ @param Buffer Memory buffer for DMA transfer.\r
+ @param BlockSize the number of the bytes in one block.\r
+ @param NumberOfBlocks Number of blocks to read.\r
+\r
+**/\r
+VOID\r
+SetDMA (\r
+ IN FDC_BLK_IO_DEV *FdcBlkIoDev,\r
+ IN VOID *Buffer,\r
+ IN UINTN BlockSize,\r
+ IN UINTN NumberOfBlocks\r
+ )\r
+{\r
+ UINT8 Data;\r
+ UINTN Count;\r
+\r
+ //\r
+ // Mask DMA channel 2;\r
+ //\r
+ IoWrite8 (R_8237_DMA_WRSMSK_CH0_3, B_8237_DMA_WRSMSK_CMS | 2);\r
+\r
+ //\r
+ // Clear first/last flip flop\r
+ //\r
+ IoWrite8 (R_8237_DMA_CBPR_CH0_3, B_8237_DMA_WRSMSK_CMS | 2);\r
+\r
+ //\r
+ // Set mode\r
+ //\r
+ IoWrite8 (R_8237_DMA_CHMODE_CH0_3, V_8237_DMA_CHMODE_SINGLE | V_8237_DMA_CHMODE_IO2MEM | 2);\r
+\r
+ //\r
+ // Set base address and page register\r
+ //\r
+ Data = (UINT8) (UINTN) Buffer;\r
+ IoWrite8 (R_8237_DMA_BASE_CA_CH2, Data);\r
+ Data = (UINT8) ((UINTN) Buffer >> 8);\r
+ IoWrite8 (R_8237_DMA_BASE_CA_CH2, Data);\r
+\r
+ Data = (UINT8) ((UINTN) Buffer >> 16);\r
+ IoWrite8 (R_8237_DMA_MEM_LP_CH2, Data);\r
+\r
+ //\r
+ // Set count register\r
+ //\r
+ Count = BlockSize * NumberOfBlocks - 1;\r
+ Data = (UINT8) (Count & 0xff);\r
+ IoWrite8 (R_8237_DMA_BASE_CC_CH2, Data);\r
+ Data = (UINT8) (Count >> 8);\r
+ IoWrite8 (R_8237_DMA_BASE_CC_CH2, Data);\r
+\r
+ //\r
+ // Clear channel 2 mask\r
+ //\r
+ IoWrite8 (R_8237_DMA_WRSMSK_CH0_3, 0x02);\r
+}\r
+\r
+\r
+/**\r
+ According to the block range specified by Lba and NumberOfBlocks, calculate\r
+ the number of blocks in the same sector, which can be transferred in a batch.\r
+\r
+ @param Info Information of floppy device.\r
+ @param Lba Start address of block range.\r
+ @param NumberOfBlocks Number of blocks of the range.\r
+\r
+ @return Number of blocks in the same sector.\r
+\r
+**/\r
+UINTN\r
+GetTransferBlockCount (\r
+ IN PEI_FLOPPY_DEVICE_INFO *Info,\r
+ IN EFI_PEI_LBA Lba,\r
+ IN UINTN NumberOfBlocks\r
+ )\r
+{\r
+ DISKET_PARA_TABLE *Para;\r
+ UINT8 EndOfTrack;\r
+ UINT8 Head;\r
+ UINT8 SectorsInTrack;\r
+\r
+ //\r
+ // Get the base of disk parameter information corresponding to its type.\r
+ //\r
+ Para = (DISKET_PARA_TABLE *) ((UINT8 *) DiskPara + sizeof (DISKET_PARA_TABLE) * Info->Type);\r
+\r
+ EndOfTrack = Para->EndOfTrack;\r
+ Head = (UINT8) ((UINTN) Lba / EndOfTrack % 2);\r
+\r
+ SectorsInTrack = (UINT8) (EndOfTrack * (2 - Head) - (UINT8) ((UINTN) Lba % EndOfTrack));\r
+ if (SectorsInTrack < NumberOfBlocks) {\r
+ //\r
+ // Not all the block range locates in the same sector\r
+ //\r
+ return SectorsInTrack;\r
+ } else {\r
+ //\r
+ // All the block range is in the same sector.\r
+ //\r
+ return NumberOfBlocks;\r
+ }\r
+}\r
+\r
+/**\r
+ Read data sector from FDC device.\r
+\r
+ @param FdcBlkIoDev Instance of FDC_BLK_IO_DEV.\r
+ @param Info Information of floppy device.\r
+ @param Buffer Buffer to setup for DMA.\r
+ @param Lba The start address to read.\r
+ @param NumberOfBlocks Number of blocks to read.\r
+\r
+ @retval EFI_SUCCESS Data successfully read out.\r
+ @retval EFI_DEVICE_ERROR FDC device has errors.\r
+ @retval EFI_TIMEOUT Command does not take effect in time.\r
+\r
+**/\r
+EFI_STATUS\r
+ReadDataSector (\r
+ IN FDC_BLK_IO_DEV *FdcBlkIoDev,\r
+ IN OUT PEI_FLOPPY_DEVICE_INFO *Info,\r
+ IN VOID *Buffer,\r
+ IN EFI_PEI_LBA Lba,\r
+ IN UINTN NumberOfBlocks\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ FDC_COMMAND_PACKET1 Command;\r
+ FDC_RESULT_PACKET Result;\r
+ UINTN Index;\r
+ UINTN Times;\r
+ UINT8 *Pointer;\r
+\r
+ Status = Seek (FdcBlkIoDev, Info, Lba);\r
+ if (Status != EFI_SUCCESS) {\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+\r
+ //\r
+ // Set up DMA\r
+ //\r
+ SetDMA (FdcBlkIoDev, Buffer, Info->MediaInfo.BlockSize, NumberOfBlocks);\r
+\r
+ //\r
+ // Allocate Read command packet\r
+ //\r
+ ZeroMem (&Command, sizeof (FDC_COMMAND_PACKET1));\r
+ Command.CommandCode = READ_DATA_CMD | CMD_MT | CMD_MFM | CMD_SK;\r
+\r
+ //\r
+ // Fill parameters for command.\r
+ //\r
+ FillPara (Info, Lba, &Command);\r
+\r
+ //\r
+ // Write command bytes to FDC\r
+ //\r
+ Pointer = (UINT8 *) (&Command);\r
+ for (Index = 0; Index < sizeof (FDC_COMMAND_PACKET1); Index++) {\r
+ if (DataOutByte (FdcBlkIoDev, Pointer++) != EFI_SUCCESS) {\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+ }\r
+\r
+ //\r
+ // Wait for some time until command takes effect.\r
+ //\r
+ Times = (STALL_1_SECOND / FDC_CHECK_INTERVAL) + 1;\r
+ do {\r
+ if ((IoRead8 ((UINT16) (PcdGet16 (PcdFdcBaseAddress) + FDC_REGISTER_MSR)) & 0xc0) == 0xc0) {\r
+ break;\r
+ }\r
+\r
+ MicroSecondDelay (FDC_SHORT_DELAY);\r
+ } while (--Times > 0);\r
+\r
+ if (Times == 0) {\r
+ //\r
+ // Command fails to take effect in time, return EFI_TIMEOUT.\r
+ //\r
+ return EFI_TIMEOUT;\r
+ }\r
+\r
+ //\r
+ // Read result bytes from FDC\r
+ //\r
+ Pointer = (UINT8 *) (&Result);\r
+ for (Index = 0; Index < sizeof (FDC_RESULT_PACKET); Index++) {\r
+ if (DataInByte (FdcBlkIoDev, Pointer++) != EFI_SUCCESS) {\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+ }\r
+\r
+ return CheckResult (&Result, Info);\r
+}\r
+\r
+/**\r
+ Gets the count of block I/O devices that one specific block driver detects.\r
+\r
+ This function is used for getting the count of block I/O devices that one \r
+ specific block driver detects. To the PEI ATAPI driver, it returns the number\r
+ of all the detected ATAPI devices it detects during the enumeration process. \r
+ To the PEI legacy floppy driver, it returns the number of all the legacy \r
+ devices it finds during its enumeration process. If no device is detected, \r
+ then the function will return zero. \r
+ \r
+ @param[in] PeiServices General-purpose services that are available \r
+ to every PEIM.\r
+ @param[in] This Indicates the EFI_PEI_RECOVERY_BLOCK_IO_PPI \r
+ instance.\r
+ @param[out] NumberBlockDevices The number of block I/O devices discovered.\r
+\r
+ @retval EFI_SUCCESS Operation performed successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+FdcGetNumberOfBlockDevices (\r
+ IN EFI_PEI_SERVICES **PeiServices,\r
+ IN EFI_PEI_RECOVERY_BLOCK_IO_PPI *This,\r
+ OUT UINTN *NumberBlockDevices\r
+ )\r
+{\r
+ FDC_BLK_IO_DEV *FdcBlkIoDev;\r
+\r
+ FdcBlkIoDev = NULL;\r
+\r
+ FdcBlkIoDev = PEI_RECOVERY_FDC_FROM_BLKIO_THIS (This);\r
+\r
+ *NumberBlockDevices = FdcBlkIoDev->DeviceCount;\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Gets a block device's media information.\r
+\r
+ This function will provide the caller with the specified block device's media \r
+ information. If the media changes, calling this function will update the media \r
+ information accordingly.\r
+\r
+ @param[in] PeiServices General-purpose services that are available to every\r
+ PEIM\r
+ @param[in] This Indicates the EFI_PEI_RECOVERY_BLOCK_IO_PPI instance.\r
+ @param[in] DeviceIndex Specifies the block device to which the function wants \r
+ to talk. Because the driver that implements Block I/O \r
+ PPIs will manage multiple block devices, the PPIs that \r
+ want to talk to a single device must specify the \r
+ device index that was assigned during the enumeration\r
+ process. This index is a number from one to \r
+ NumberBlockDevices.\r
+ @param[out] MediaInfo The media information of the specified block media. \r
+ The caller is responsible for the ownership of this \r
+ data structure.\r
+ \r
+ @retval EFI_SUCCESS Media information about the specified block device \r
+ was obtained successfully.\r
+ @retval EFI_DEVICE_ERROR Cannot get the media information due to a hardware \r
+ error.\r
+ @retval Others Other failure occurs.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+FdcGetBlockDeviceMediaInfo (\r
+ IN EFI_PEI_SERVICES **PeiServices,\r
+ IN EFI_PEI_RECOVERY_BLOCK_IO_PPI *This,\r
+ IN UINTN DeviceIndex,\r
+ OUT EFI_PEI_BLOCK_IO_MEDIA *MediaInfo\r
+ )\r
+{\r
+ UINTN DeviceCount;\r
+ FDC_BLK_IO_DEV *FdcBlkIoDev;\r
+ BOOLEAN Healthy;\r
+ UINTN Index;\r
+\r
+ FdcBlkIoDev = NULL;\r
+\r
+ if (This == NULL || MediaInfo == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ FdcBlkIoDev = PEI_RECOVERY_FDC_FROM_BLKIO_THIS (This);\r
+\r
+ DeviceCount = FdcBlkIoDev->DeviceCount;\r
+\r
+ //\r
+ // DeviceIndex is a value from 1 to NumberBlockDevices.\r
+ //\r
+ if ((DeviceIndex < 1) || (DeviceIndex > DeviceCount) || (DeviceIndex > 2)) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ Index = DeviceIndex - 1;\r
+ //\r
+ // Probe media and retrieve latest media information\r
+ //\r
+ Healthy = DiscoverFdcDevice (\r
+ FdcBlkIoDev,\r
+ &FdcBlkIoDev->DeviceInfo[Index],\r
+ MediaInfo\r
+ );\r
+\r
+ if (!Healthy) {\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+\r
+ CopyMem (\r
+ &(FdcBlkIoDev->DeviceInfo[Index].MediaInfo),\r
+ MediaInfo,\r
+ sizeof (EFI_PEI_BLOCK_IO_MEDIA)\r
+ );\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Reads the requested number of blocks from the specified block device.\r
+\r
+ The function reads the requested number of blocks from the device. All the \r
+ blocks are read, or an error is returned. If there is no media in the device,\r
+ the function returns EFI_NO_MEDIA.\r
+\r
+ @param[in] PeiServices General-purpose services that are available to \r
+ every PEIM.\r
+ @param[in] This Indicates the EFI_PEI_RECOVERY_BLOCK_IO_PPI instance.\r
+ @param[in] DeviceIndex Specifies the block device to which the function wants \r
+ to talk. Because the driver that implements Block I/O \r
+ PPIs will manage multiple block devices, the PPIs that \r
+ want to talk to a single device must specify the device \r
+ index that was assigned during the enumeration process. \r
+ This index is a number from one to NumberBlockDevices.\r
+ @param[in] StartLBA The starting logical block address (LBA) to read from\r
+ on the device\r
+ @param[in] BufferSize The size of the Buffer in bytes. This number must be\r
+ a multiple of the intrinsic block size of the device.\r
+ @param[out] Buffer A pointer to the destination buffer for the data.\r
+ The caller is responsible for the ownership of the \r
+ buffer.\r
+ \r
+ @retval EFI_SUCCESS The data was read correctly from the device.\r
+ @retval EFI_DEVICE_ERROR The device reported an error while attempting \r
+ to perform the read operation.\r
+ @retval EFI_INVALID_PARAMETER The read request contains LBAs that are not \r
+ valid, or the buffer is not properly aligned.\r
+ @retval EFI_NO_MEDIA There is no media in the device.\r
+ @retval EFI_BAD_BUFFER_SIZE The BufferSize parameter is not a multiple of\r
+ the intrinsic block size of the device.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+FdcReadBlocks (\r
+ IN EFI_PEI_SERVICES **PeiServices,\r
+ IN EFI_PEI_RECOVERY_BLOCK_IO_PPI *This,\r
+ IN UINTN DeviceIndex,\r
+ IN EFI_PEI_LBA StartLBA,\r
+ IN UINTN BufferSize,\r
+ OUT VOID *Buffer\r
+ )\r
+{\r
+ EFI_PEI_BLOCK_IO_MEDIA MediaInfo;\r
+ EFI_STATUS Status;\r
+ UINTN Count;\r
+ UINTN NumberOfBlocks;\r
+ UINTN BlockSize;\r
+ FDC_BLK_IO_DEV *FdcBlkIoDev;\r
+ VOID *MemPage;\r
+\r
+ FdcBlkIoDev = NULL;\r
+\r
+ if (This == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ FdcBlkIoDev = PEI_RECOVERY_FDC_FROM_BLKIO_THIS (This);\r
+\r
+ if (Buffer == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ Status = FdcGetBlockDeviceMediaInfo (PeiServices, This, DeviceIndex, &MediaInfo);\r
+ if (Status != EFI_SUCCESS) {\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+\r
+ if (!MediaInfo.MediaPresent) {\r
+ return EFI_NO_MEDIA;\r
+ }\r
+\r
+ BlockSize = MediaInfo.BlockSize;\r
+\r
+ //\r
+ // If BufferSize cannot be divided by block size of FDC device,\r
+ // return EFI_BAD_BUFFER_SIZE.\r
+ //\r
+ if (BufferSize % BlockSize != 0) {\r
+ return EFI_BAD_BUFFER_SIZE;\r
+ }\r
+\r
+ NumberOfBlocks = BufferSize / BlockSize;\r
+\r
+ if ((StartLBA + NumberOfBlocks - 1) > FdcBlkIoDev->DeviceInfo[DeviceIndex - 1].MediaInfo.LastBlock) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ MemPage = AllocatePages (EFI_SIZE_TO_PAGES (BufferSize));\r
+ if ((MemPage == NULL) || ((UINTN) MemPage >= ISA_MAX_MEMORY_ADDRESS)) {\r
+ //\r
+ // If fail to allocate memory under ISA_MAX_MEMORY_ADDRESS, designate the address space for DMA\r
+ //\r
+ MemPage = (VOID *) ((UINTN) (UINT32) 0x0f00000);\r
+ }\r
+ Status = MotorOn (FdcBlkIoDev, &(FdcBlkIoDev->DeviceInfo[DeviceIndex - 1]));\r
+ if (Status != EFI_SUCCESS) {\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+\r
+ Status = Setup (FdcBlkIoDev, FdcBlkIoDev->DeviceInfo[DeviceIndex - 1].DevPos);\r
+ if (Status != EFI_SUCCESS) {\r
+ MotorOff (FdcBlkIoDev, &(FdcBlkIoDev->DeviceInfo[DeviceIndex - 1]));\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+ //\r
+ // Read data in batches.\r
+ // Blocks in the same cylinder are read out in a batch.\r
+ //\r
+ while ((Count = GetTransferBlockCount (\r
+ &(FdcBlkIoDev->DeviceInfo[DeviceIndex - 1]),\r
+ StartLBA,\r
+ NumberOfBlocks\r
+ )) != 0 && Status == EFI_SUCCESS) {\r
+ Status = ReadDataSector (\r
+ FdcBlkIoDev,\r
+ &(FdcBlkIoDev->DeviceInfo[DeviceIndex - 1]),\r
+ MemPage,\r
+ StartLBA,\r
+ Count\r
+ );\r
+ CopyMem (Buffer, MemPage, BlockSize * Count);\r
+ StartLBA += Count;\r
+ NumberOfBlocks -= Count;\r
+ Buffer = (VOID *) ((UINTN) Buffer + Count * BlockSize);\r
+ }\r
+\r
+ MotorOff (FdcBlkIoDev, &(FdcBlkIoDev->DeviceInfo[DeviceIndex - 1]));\r
+\r
+ switch (Status) {\r
+ case EFI_SUCCESS:\r
+ return EFI_SUCCESS;\r
+\r
+ default:\r
+ FdcReset (FdcBlkIoDev, FdcBlkIoDev->DeviceInfo[DeviceIndex - 1].DevPos);\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+}\r
+\r
+/**\r
+ Initializes the floppy disk controller and installs FDC Block I/O PPI.\r
+\r
+ @param FileHandle Handle of the file being invoked.\r
+ @param PeiServices Describes the list of possible PEI Services.\r
+\r
+ @retval EFI_SUCCESS Successfully initialized FDC and installed PPI.\r
+ @retval EFI_NOT_FOUND Cannot find FDC device.\r
+ @retval EFI_OUT_OF_RESOURCES Have no enough memory to create instance or descriptors.\r
+ @retval Other Fail to install FDC Block I/O PPI.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+FdcPeimEntry (\r
+ IN EFI_PEI_FILE_HANDLE FileHandle,\r
+ IN CONST EFI_PEI_SERVICES **PeiServices\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ FDC_BLK_IO_DEV *FdcBlkIoDev;\r
+ UINTN DeviceCount;\r
+ UINT32 Index;\r
+\r
+ Status = PeiServicesRegisterForShadow (FileHandle);\r
+ if (!EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ //\r
+ // Allocate memory for instance of FDC_BLK_IO_DEV and copy initial value\r
+ // from template to it. \r
+ //\r
+ FdcBlkIoDev = AllocatePages (EFI_SIZE_TO_PAGES(sizeof (FDC_BLK_IO_DEV)));\r
+ if (FdcBlkIoDev == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+ CopyMem (FdcBlkIoDev, &mBlockIoDevTemplate, sizeof (mBlockIoDevTemplate));\r
+\r
+ //\r
+ // Initialize DMA controller to enable all channels.\r
+ //\r
+ for (Index = 0; Index < sizeof (mRegisterTable) / sizeof (PEI_DMA_TABLE); Index++) {\r
+ IoWrite8 (mRegisterTable[Index].Register, mRegisterTable[Index].Value);\r
+ }\r
+ REPORT_STATUS_CODE (EFI_PROGRESS_CODE, EFI_PERIPHERAL_REMOVABLE_MEDIA + EFI_P_PC_INIT);\r
+\r
+ //\r
+ // Enumerate FDC devices.\r
+ //\r
+ DeviceCount = FdcEnumeration (FdcBlkIoDev);\r
+ if (DeviceCount == 0) {\r
+ return EFI_NOT_FOUND;\r
+ }\r
+\r
+ FdcBlkIoDev->PpiDescriptor.Ppi = &FdcBlkIoDev->FdcBlkIo;\r
+\r
+ return PeiServicesInstallPpi (&FdcBlkIoDev->PpiDescriptor);\r
+}\r