summaryrefslogtreecommitdiff
path: root/IntelFrameworkModulePkg/Bus
diff options
context:
space:
mode:
authorjljusten <jljusten@6f19259b-4bc3-4df7-8a09-765794883524>2011-06-17 17:42:49 +0000
committerjljusten <jljusten@6f19259b-4bc3-4df7-8a09-765794883524>2011-06-17 17:42:49 +0000
commitd8387fa4af2abae05dc6ec502b27adea455cdaaa (patch)
tree3d77265b9951a80c7e1dbd466778611adc381ae4 /IntelFrameworkModulePkg/Bus
parenta1e23d485b83190d1831f70553f5f15435dcdf20 (diff)
IntelFrameworkModulePkg: Add IsaFloppyPei driver
Signed-off-by: jljusten Reviewed-by: mdkinney git-svn-id: https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2@11845 6f19259b-4bc3-4df7-8a09-765794883524
Diffstat (limited to 'IntelFrameworkModulePkg/Bus')
-rw-r--r--IntelFrameworkModulePkg/Bus/Isa/IsaFloppyPei/Fdc.h235
-rw-r--r--IntelFrameworkModulePkg/Bus/Isa/IsaFloppyPei/FloppyPeim.c1768
-rw-r--r--IntelFrameworkModulePkg/Bus/Isa/IsaFloppyPei/FloppyPeim.h246
-rw-r--r--IntelFrameworkModulePkg/Bus/Isa/IsaFloppyPei/IsaFloppyPei.inf68
4 files changed, 2317 insertions, 0 deletions
diff --git a/IntelFrameworkModulePkg/Bus/Isa/IsaFloppyPei/Fdc.h b/IntelFrameworkModulePkg/Bus/Isa/IsaFloppyPei/Fdc.h
new file mode 100644
index 0000000000..a6a7e42aee
--- /dev/null
+++ b/IntelFrameworkModulePkg/Bus/Isa/IsaFloppyPei/Fdc.h
@@ -0,0 +1,235 @@
+/** @file
+Definition of FDC registers and structures.
+
+Copyright (c) 2006 - 2010, Intel Corporation. All rights reserved.<BR>
+
+This program and the accompanying materials
+are licensed and made available under the terms and conditions
+of the BSD License which accompanies this distribution. The
+full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef _PEI_RECOVERY_FDC_H_
+#define _PEI_RECOVERY_FDC_H_
+
+//
+// FDC Registers
+//
+#define FDC_REGISTER_DOR 2 //Digital Output Register
+#define FDC_REGISTER_MSR 4 //Main Status Register
+#define FDC_REGISTER_DTR 5 //Data Register
+#define FDC_REGISTER_CCR 7 //Configuration Control Register(data rate select)
+#define FDC_REGISTER_DIR 7 //Digital Input Register(diskchange)
+//
+// FDC Register Bit Definitions
+//
+//
+// Digital Out Register(WO)
+//
+#define SELECT_DRV BIT0 // Select Drive: 0=A 1=B
+#define RESET_FDC BIT2 // Reset FDC
+#define INT_DMA_ENABLE BIT3 // Enable Int & DMA
+#define DRVA_MOTOR_ON BIT4 // Turn On Drive A Motor
+#define DRVB_MOTOR_ON BIT5 // Turn On Drive B Motor
+//
+// Main Status Register(RO)
+//
+#define MSR_DAB BIT0 // Drive A Busy
+#define MSR_DBB BIT1 // Drive B Busy
+#define MSR_CB BIT4 // FDC Busy
+#define MSR_NDM BIT5 // Non-DMA Mode
+#define MSR_DIO BIT6 // Data Input/Output
+#define MSR_RQM BIT7 // Request For Master
+//
+// Configuration Control Register(WO)
+//
+#define CCR_DRC (BIT0 | BIT1) // Data Rate select
+//
+// Digital Input Register(RO)
+//
+#define DIR_DCL BIT7 // Disk change line
+#define DRC_500KBS 0x0 // 500K
+#define DRC_300KBS 0x01 // 300K
+#define DRC_250KBS 0x02 // 250K
+//
+// FDC Command Code
+//
+#define READ_DATA_CMD 0x06
+#define SEEK_CMD 0x0F
+#define RECALIBRATE_CMD 0x07
+#define SENSE_INT_STATUS_CMD 0x08
+#define SPECIFY_CMD 0x03
+#define SENSE_DRV_STATUS_CMD 0x04
+
+///
+/// CMD_MT: Multi_Track Selector
+/// when set , this flag selects the multi-track operating mode.
+/// In this mode, the FDC treats a complete cylinder under head0 and 1 as a single track
+///
+#define CMD_MT BIT7
+
+///
+/// CMD_MFM: MFM/FM Mode Selector
+/// A one selects the double density(MFM) mode
+/// A zero selects single density (FM) mode
+///
+#define CMD_MFM BIT6
+
+///
+/// CMD_SK: Skip Flag
+/// When set to 1, sectors containing a deleted data address mark will automatically be skipped
+/// during the execution of Read Data.
+/// When set to 0, the sector is read or written the same as the read and write commands.
+///
+#define CMD_SK BIT5
+
+//
+// FDC Status Register Bit Definitions
+//
+//
+// Status Register 0
+//
+#define STS0_IC (BIT7 | BIT6) // Interrupt Code
+#define STS0_SE BIT5 // Seek End: the FDC completed a seek or recalibrate command
+#define STS0_EC BIT4 // Equipment Check
+#define STS0_NR BIT3 // Not Ready(unused), this bit is always 0
+#define STS0_HA BIT2 // Head Address: the current head address
+//
+// STS0_US1 & STS0_US0: Drive Select(the current selected drive)
+//
+#define STS0_US1 BIT1 // Unit Select1
+#define STS0_US0 BIT0 // Unit Select0
+//
+// Status Register 1
+//
+#define STS1_EN BIT7 // End of Cylinder
+//
+// BIT6 is unused
+//
+#define STS1_DE BIT5 // Data Error: The FDC detected a CRC error in either the ID field or data field of a sector
+#define STS1_OR BIT4 // Overrun/Underrun: Becomes set if FDC does not receive CPU or DMA service within the required time interval
+//
+// BIT3 is unused
+//
+#define STS1_ND BIT2 // No data
+#define STS1_NW BIT1 // Not Writable
+#define STS1_MA BIT0 // Missing Address Mark
+
+//
+// Status Register 2
+//
+// BIT7 is unused
+//
+#define STS2_CM BIT6 // Control Mark
+#define STS2_DD BIT5 // Data Error in Data Field: The FDC detected a CRC error in the data field
+#define STS2_WC BIT4 // Wrong Cylinder: The track address from sector ID field is different from the track address maintained inside FDC
+//
+// BIT3 is unused
+// BIT2 is unused
+//
+#define STS2_BC BIT1 // Bad Cylinder
+#define STS2_MD BIT0 // Missing Address Mark in DataField
+
+//
+// Status Register 3
+//
+// BIT7 is unused
+//
+#define STS3_WP BIT6 // Write Protected
+//
+// BIT5 is unused
+//
+#define STS3_T0 BIT4 // Track 0
+//
+// BIT3 is unused
+//
+#define STS3_HD BIT2 // Head Address
+//
+// STS3_US1 & STS3_US0 : Drive Select
+//
+#define STS3_US1 BIT1 // Unit Select1
+#define STS3_US0 BIT0 // Unit Select0
+
+//
+// Status Register 0 Interrupt Code Description
+//
+#define IC_NT 0x0 // Normal Termination of Command
+#define IC_AT 0x40 // Abnormal Termination of Command
+#define IC_IC 0x80 // Invalid Command
+#define IC_ATRC 0xC0 // Abnormal Termination caused by Polling
+
+///
+/// Table of parameters for diskette
+///
+typedef struct {
+ UINT8 EndOfTrack; ///< End of track
+ UINT8 GapLength; ///< Gap length
+ UINT8 DataLength; ///< Data length
+ UINT8 Number; ///< Number of bytes per sector
+ UINT8 MaxTrackNum;
+ UINT8 MotorStartTime;
+ UINT8 MotorOffTime;
+ UINT8 HeadSettlingTime;
+ UINT8 DataTransferRate;
+} DISKET_PARA_TABLE;
+
+///
+/// Structure for FDC Command Packet 1
+///
+typedef struct {
+ UINT8 CommandCode;
+ UINT8 DiskHeadSel;
+ UINT8 Cylinder;
+ UINT8 Head;
+ UINT8 Sector;
+ UINT8 Number;
+ UINT8 EndOfTrack;
+ UINT8 GapLength;
+ UINT8 DataLength;
+} FDC_COMMAND_PACKET1;
+
+///
+/// Structure for FDC Command Packet 2
+///
+typedef struct {
+ UINT8 CommandCode;
+ UINT8 DiskHeadSel;
+} FDC_COMMAND_PACKET2;
+
+///
+/// Structure for FDC Specify Command
+///
+typedef struct {
+ UINT8 CommandCode;
+ UINT8 SrtHut;
+ UINT8 HltNd;
+} FDC_SPECIFY_CMD;
+
+///
+/// Structure for FDC Seek Command
+///
+typedef struct {
+ UINT8 CommandCode;
+ UINT8 DiskHeadSel;
+ UINT8 NewCylinder;
+} FDC_SEEK_CMD;
+
+///
+/// Structure for FDC Result Packet
+///
+typedef struct {
+ UINT8 Status0;
+ UINT8 Status1;
+ UINT8 Status2;
+ UINT8 CylinderNumber;
+ UINT8 HeaderAddress;
+ UINT8 Record;
+ UINT8 Number;
+} FDC_RESULT_PACKET;
+
+#endif
diff --git a/IntelFrameworkModulePkg/Bus/Isa/IsaFloppyPei/FloppyPeim.c b/IntelFrameworkModulePkg/Bus/Isa/IsaFloppyPei/FloppyPeim.c
new file mode 100644
index 0000000000..afeb2213c0
--- /dev/null
+++ b/IntelFrameworkModulePkg/Bus/Isa/IsaFloppyPei/FloppyPeim.c
@@ -0,0 +1,1768 @@
+/** @file
+Floppy Peim to support Recovery function from Floppy device.
+
+Copyright (c) 2006 - 2010, Intel Corporation. All rights reserved.<BR>
+
+This program and the accompanying materials
+are licensed and made available under the terms and conditions
+of the BSD License which accompanies this distribution. The
+full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+
+#include "FloppyPeim.h"
+
+
+PEI_DMA_TABLE mRegisterTable[] = {
+ //
+ // DMA2: Clear Byte Ptr, Enable
+ //
+ {
+ R_8237_DMA_CBPR_CH4_7,
+ 0
+ },
+ {
+ R_8237_DMA_COMMAND_CH4_7,
+ 0
+ },
+ //
+ // DMA1: Clear Byte Ptr, Enable
+ //
+ {
+ R_8237_DMA_CBPR_CH0_3,
+ 0
+ },
+ {
+ R_8237_DMA_COMMAND_CH0_3,
+ 0
+ },
+ //
+ // Configure Channel 4 for Cascade Mode
+ // Clear DMA Request and enable DREQ
+ //
+ {
+ R_8237_DMA_CHMODE_CH4_7,
+ V_8237_DMA_CHMODE_CASCADE | 0
+ },
+ {
+ R_8237_DMA_STA_CH4_7,
+ 0
+ },
+ {
+ R_8237_DMA_WRSMSK_CH4_7,
+ 0
+ },
+ //
+ // Configure DMA1 (Channels 0-3) for Single Mode
+ // Clear DMA Request and enable DREQ
+ //
+ {
+ R_8237_DMA_CHMODE_CH0_3,
+ V_8237_DMA_CHMODE_SINGLE | 0
+ },
+ {
+ R_8237_DMA_STA_CH0_3,
+ 0
+ },
+ {
+ R_8237_DMA_WRSMSK_CH0_3,
+ 0
+ },
+ {
+ R_8237_DMA_CHMODE_CH0_3,
+ V_8237_DMA_CHMODE_SINGLE | 1
+ },
+ {
+ R_8237_DMA_STA_CH0_3,
+ 1
+ },
+ {
+ R_8237_DMA_WRSMSK_CH0_3,
+ 1
+ },
+ {
+ R_8237_DMA_CHMODE_CH0_3,
+ V_8237_DMA_CHMODE_SINGLE | 2
+ },
+ {
+ R_8237_DMA_STA_CH0_3,
+ 2
+ },
+ {
+ R_8237_DMA_WRSMSK_CH0_3,
+ 2
+ },
+ {
+ R_8237_DMA_CHMODE_CH0_3,
+ V_8237_DMA_CHMODE_SINGLE | 3
+ },
+ {
+ R_8237_DMA_STA_CH0_3,
+ 3
+ },
+ {
+ R_8237_DMA_WRSMSK_CH0_3,
+ 3
+ },
+ //
+ // Configure DMA2 (Channels 5-7) for Single Mode
+ // Clear DMA Request and enable DREQ
+ //
+ {
+ R_8237_DMA_CHMODE_CH4_7,
+ V_8237_DMA_CHMODE_SINGLE | 1
+ },
+ {
+ R_8237_DMA_STA_CH4_7,
+ 1
+ },
+ {
+ R_8237_DMA_WRSMSK_CH4_7,
+ 1
+ },
+ {
+ R_8237_DMA_CHMODE_CH4_7,
+ V_8237_DMA_CHMODE_SINGLE | 2
+ },
+ {
+ R_8237_DMA_STA_CH4_7,
+ 2
+ },
+ {
+ R_8237_DMA_WRSMSK_CH4_7,
+ 2
+ },
+ {
+ R_8237_DMA_CHMODE_CH4_7,
+ V_8237_DMA_CHMODE_SINGLE | 3
+ },
+ {
+ R_8237_DMA_STA_CH4_7,
+ 3
+ },
+ {
+ R_8237_DMA_WRSMSK_CH4_7,
+ 3
+ }
+};
+
+//
+// Table of diskette parameters of various diskette types
+//
+DISKET_PARA_TABLE DiskPara[9] = {
+ {
+ 0x09,
+ 0x50,
+ 0xff,
+ 0x2,
+ 0x27,
+ 0x4,
+ 0x25,
+ 0x14,
+ 0x80
+ },
+ {
+ 0x09,
+ 0x2a,
+ 0xff,
+ 0x2,
+ 0x27,
+ 0x4,
+ 0x25,
+ 0x0f,
+ 0x40
+ },
+ {
+ 0x0f,
+ 0x54,
+ 0xff,
+ 0x2,
+ 0x4f,
+ 0x4,
+ 0x25,
+ 0x0f,
+ 0x0
+ },
+ {
+ 0x09,
+ 0x50,
+ 0xff,
+ 0x2,
+ 0x4f,
+ 0x4,
+ 0x25,
+ 0x0f,
+ 0x80
+ },
+ {
+ 0x09,
+ 0x2a,
+ 0xff,
+ 0x2,
+ 0x4f,
+ 0x4,
+ 0x25,
+ 0x0f,
+ 0x80
+ },
+ {
+ 0x12,
+ 0x1b,
+ 0xff,
+ 0x2,
+ 0x4f,
+ 0x4,
+ 0x25,
+ 0x0f,
+ 0x0
+ },
+ {
+ 0x09,
+ 0x2a,
+ 0xff,
+ 0x2,
+ 0x4f,
+ 0x4,
+ 0x25,
+ 0x0f,
+ 0x80
+ },
+ {
+ 0x12,
+ 0x1b,
+ 0xff,
+ 0x2,
+ 0x4f,
+ 0x4,
+ 0x25,
+ 0x0f,
+ 0x0
+ },
+ {
+ 0x24,
+ 0x1b,
+ 0xff,
+ 0x2,
+ 0x4f,
+ 0x4,
+ 0x25,
+ 0x0f,
+ 0xc0
+ }
+};
+
+//
+// Byte per sector corresponding to various device types.
+//
+UINTN BytePerSector[6] = { 0, 256, 512, 1024, 2048, 4096 };
+
+FDC_BLK_IO_DEV mBlockIoDevTemplate = {
+ FDC_BLK_IO_DEV_SIGNATURE,
+ {
+ FdcGetNumberOfBlockDevices,
+ FdcGetBlockDeviceMediaInfo,
+ FdcReadBlocks,
+ },
+ {
+ (EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST),
+ &gEfiPeiVirtualBlockIoPpiGuid,
+ NULL
+ },
+ 0,
+ {{0}}
+};
+
+/**
+ Wait and check if bits for DIO and RQM of FDC Main Status Register
+ indicates FDC is ready for read or write.
+
+ Before writing to FDC or reading from FDC, the Host must examine
+ the bit7(RQM) and bit6(DIO) of the Main Status Register.
+ That is to say:
+ Command bytes can not be written to Data Register unless RQM is 1 and DIO is 0.
+ Result bytes can not be read from Data Register unless RQM is 1 and DIO is 1.
+
+ @param FdcBlkIoDev Instance of FDC_BLK_IO_DEV.
+ @param DataIn Indicates data input or output.
+ TRUE means input.
+ FALSE means output.
+ @param TimeoutInMseconds Timeout value to wait.
+
+ @retval EFI_SUCCESS FDC is ready.
+ @retval EFI_NOT_READY FDC is not ready within the specified time period.
+
+**/
+EFI_STATUS
+FdcDRQReady (
+ IN FDC_BLK_IO_DEV *FdcBlkIoDev,
+ IN BOOLEAN DataIn,
+ IN UINTN TimeoutInMseconds
+ )
+{
+ UINTN Delay;
+ UINT8 StatusRegister;
+ UINT8 BitInOut;
+
+ //
+ // Check bit6 of Main Status Register.
+ //
+ BitInOut = 0;
+ if (DataIn) {
+ BitInOut = BIT6;
+ }
+
+ Delay = ((TimeoutInMseconds * STALL_1_MSECOND) / FDC_CHECK_INTERVAL) + 1;
+ do {
+ StatusRegister = IoRead8 ((UINT16) (PcdGet16 (PcdFdcBaseAddress) + FDC_REGISTER_MSR));
+ if ((StatusRegister & MSR_RQM) == MSR_RQM && (StatusRegister & MSR_DIO) == BitInOut) {
+ //
+ // FDC is ready
+ //
+ break;
+ }
+
+ MicroSecondDelay (FDC_SHORT_DELAY);
+ } while (--Delay > 0);
+
+ if (Delay == 0) {
+ //
+ // FDC is not ready within the specified time period
+ //
+ return EFI_NOT_READY;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Read a byte from FDC data register.
+
+ @param FdcBlkIoDev Instance of FDC_BLK_IO_DEV.
+ @param Pointer Pointer to buffer to hold data read from FDC.
+
+ @retval EFI_SUCCESS Byte successfully read.
+ @retval EFI_DEVICE_ERROR FDC is not ready.
+
+**/
+EFI_STATUS
+DataInByte (
+ IN FDC_BLK_IO_DEV *FdcBlkIoDev,
+ OUT UINT8 *Pointer
+ )
+{
+ UINT8 Data;
+
+ //
+ // Wait for 1ms and detect the FDC is ready to be read
+ //
+ if (FdcDRQReady (FdcBlkIoDev, TRUE, 1) != EFI_SUCCESS) {
+ //
+ // FDC is not ready.
+ //
+ return EFI_DEVICE_ERROR;
+ }
+
+ Data = IoRead8 ((UINT16) (PcdGet16 (PcdFdcBaseAddress) + FDC_REGISTER_DTR));
+ MicroSecondDelay (FDC_SHORT_DELAY);
+ *Pointer = Data;
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Write a byte to FDC data register.
+
+ @param FdcBlkIoDev Instance of FDC_BLK_IO_DEV.
+ @param Pointer Pointer to data to write.
+
+ @retval EFI_SUCCESS Byte successfully written.
+ @retval EFI_DEVICE_ERROR FDC is not ready.
+
+**/
+EFI_STATUS
+DataOutByte (
+ IN FDC_BLK_IO_DEV *FdcBlkIoDev,
+ IN UINT8 *Pointer
+ )
+{
+ UINT8 Data;
+
+ //
+ // Wait for 1ms and detect the FDC is ready to be written
+ //
+ if (FdcDRQReady (FdcBlkIoDev, FALSE, 1) != EFI_SUCCESS) {
+ //
+ // FDC is not ready.
+ //
+ return EFI_DEVICE_ERROR;
+ }
+
+ Data = *Pointer;
+ IoWrite8 ((UINT16) (PcdGet16 (PcdFdcBaseAddress) + FDC_REGISTER_DTR), Data);
+ MicroSecondDelay (FDC_SHORT_DELAY);
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Get Sts0 and Pcn status from FDC
+
+ @param FdcBlkIoDev Instance of FDC_BLK_IO_DEV
+ @param Sts0 Value of Sts0
+ @param Pcn Value of Pcn
+
+ @retval EFI_SUCCESS Successfully retrieved status value of Sts0 and Pcn.
+ @retval EFI_DEVICE_ERROR Fail to send SENSE_INT_STATUS_CMD.
+ @retval EFI_DEVICE_ERROR Fail to read Sts0.
+ @retval EFI_DEVICE_ERROR Fail to read Pcn.
+
+**/
+EFI_STATUS
+SenseIntStatus (
+ IN FDC_BLK_IO_DEV *FdcBlkIoDev,
+ OUT UINT8 *Sts0,
+ OUT UINT8 *Pcn
+ )
+{
+ UINT8 Command;
+
+ Command = SENSE_INT_STATUS_CMD;
+
+ if (DataOutByte (FdcBlkIoDev, &Command) != EFI_SUCCESS) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ if (DataInByte (FdcBlkIoDev, Sts0) != EFI_SUCCESS) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ if (DataInByte (FdcBlkIoDev, Pcn) != EFI_SUCCESS) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Issue Specify command.
+
+ @param FdcBlkIoDev Instance of FDC_BLK_IO_DEV.
+
+ @retval EFI_SUCCESS Specify command successfully issued.
+ @retval EFI_DEVICE_ERROR FDC device has errors.
+
+**/
+EFI_STATUS
+Specify (
+ IN FDC_BLK_IO_DEV *FdcBlkIoDev
+ )
+{
+ FDC_SPECIFY_CMD Command;
+ UINTN Index;
+ UINT8 *Pointer;
+
+ ZeroMem (&Command, sizeof (FDC_SPECIFY_CMD));
+ Command.CommandCode = SPECIFY_CMD;
+ //
+ // set SRT, HUT
+ //
+ Command.SrtHut = 0xdf;
+ //
+ // 0xdf;
+ // set HLT and DMA
+ //
+ Command.HltNd = 0x02;
+
+ Pointer = (UINT8 *) (&Command);
+ for (Index = 0; Index < sizeof (FDC_SPECIFY_CMD); Index++) {
+ if (DataOutByte (FdcBlkIoDev, Pointer++) != EFI_SUCCESS) {
+ return EFI_DEVICE_ERROR;
+ }
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Wait until busy bit is cleared.
+
+ @param FdcBlkIoDev Instance of FDC_BLK_IO_DEV.
+ @param DevPos Position of FDC (Driver A or B)
+ @param TimeoutInMseconds Timeout value to wait.
+
+ @retval EFI_SUCCESS Busy bit has been cleared before timeout.
+ @retval EFI_TIMEOUT Time goes out before busy bit is cleared.
+
+**/
+EFI_STATUS
+FdcWaitForBSYClear (
+ IN FDC_BLK_IO_DEV *FdcBlkIoDev,
+ IN UINT8 DevPos,
+ IN UINTN TimeoutInMseconds
+ )
+{
+ UINTN Delay;
+ UINT8 StatusRegister;
+ UINT8 Mask;
+
+ //
+ // How to determine drive and command are busy or not: by the bits of Main Status Register
+ // bit0: Drive 0 busy (drive A)
+ // bit1: Drive 1 busy (drive B)
+ // bit4: Command busy
+ //
+ // set mask: for drive A set bit0 & bit4; for drive B set bit1 & bit4
+ //
+ Mask = (UINT8) ((DevPos == 0 ? MSR_DAB : MSR_DBB) | MSR_CB);
+
+ Delay = ((TimeoutInMseconds * STALL_1_MSECOND) / FDC_CHECK_INTERVAL) + 1;
+
+ do {
+ StatusRegister = IoRead8 ((UINT16) (PcdGet16 (PcdFdcBaseAddress) + FDC_REGISTER_MSR));
+
+ if ((StatusRegister & Mask) == 0x00) {
+ //
+ // not busy
+ //
+ break;
+ }
+
+ MicroSecondDelay (FDC_SHORT_DELAY);
+ } while (--Delay > 0);
+
+ if (Delay == 0) {
+ return EFI_TIMEOUT;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Reset FDC device.
+
+ @param FdcBlkIoDev Instance of FDC_BLK_IO_DEV
+ @param DevPos Index of FDC device.
+
+ @retval EFI_SUCCESS FDC device successfully reset.
+ @retval EFI_DEVICE_ERROR Fail to reset FDC device.
+
+**/
+EFI_STATUS
+FdcReset (
+ IN FDC_BLK_IO_DEV *FdcBlkIoDev,
+ IN UINT8 DevPos
+ )
+{
+ UINT8 Data;
+ UINT8 Sts0;
+ UINT8 Pcn;
+ UINTN Index;
+
+ //
+ // Reset specified Floppy Logic Drive according to Fdd -> Disk
+ // Set Digital Output Register(DOR) to do reset work
+ // bit0 & bit1 of DOR : Drive Select
+ // bit2 : Reset bit
+ // bit3 : DMA and Int bit
+ // Reset : A "0" written to bit2 resets the FDC, this reset will remain active until
+ // a "1" is written to this bit.
+ // Reset step 1:
+ // use bit0 & bit1 to select the logic drive
+ // write "0" to bit2
+ //
+ Data = 0x0;
+ Data = (UINT8) (Data | (SELECT_DRV & DevPos));
+ IoWrite8 ((UINT16) (PcdGet16 (PcdFdcBaseAddress) + FDC_REGISTER_DOR), Data);
+
+ //
+ // Wait some time, at least 120us.
+ //
+ MicroSecondDelay (FDC_RESET_DELAY);
+ //
+ // Reset step 2:
+ // write "1" to bit2
+ // write "1" to bit3 : enable DMA
+ //
+ Data |= 0x0C;
+ IoWrite8 ((UINT16) (PcdGet16 (PcdFdcBaseAddress) + FDC_REGISTER_DOR), Data);
+
+ MicroSecondDelay (FDC_RESET_DELAY);
+
+ //
+ // Wait until specified floppy logic drive is not busy
+ //
+ if (FdcWaitForBSYClear (FdcBlkIoDev, DevPos, 1) != EFI_SUCCESS) {
+ return EFI_DEVICE_ERROR;
+ }
+ //
+ // Set the Transfer Data Rate
+ //
+ IoWrite8 ((UINT16) (PcdGet16 (PcdFdcBaseAddress) + FDC_REGISTER_CCR), 0x0);
+
+ MicroSecondDelay (FDC_MEDIUM_DELAY);
+
+ //
+ // Issue Sense interrupt command for each drive (totally 4 drives)
+ //
+ for (Index = 0; Index < 4; Index++) {
+ if (SenseIntStatus (FdcBlkIoDev, &Sts0, &Pcn) != EFI_SUCCESS) {
+ return EFI_DEVICE_ERROR;
+ }
+ }
+ //
+ // Issue Specify command
+ //
+ if (Specify (FdcBlkIoDev) != EFI_SUCCESS) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Turn on the motor of floppy drive.
+
+ @param FdcBlkIoDev Instance of FDC_BLK_IO_DEV.
+ @param Info Information of floppy device.
+
+ @retval EFI_SUCCESS Motor is successfully turned on.
+ @retval EFI_SUCCESS Motor is already on.
+ @retval EFI_DEVICE_ERROR Busy bit of FDC cannot be cleared.
+
+**/
+EFI_STATUS
+MotorOn (
+ IN FDC_BLK_IO_DEV *FdcBlkIoDev,
+ IN OUT PEI_FLOPPY_DEVICE_INFO *Info
+ )
+{
+ UINT8 Data;
+ UINT8 DevPos;
+
+ //
+ // Control of the floppy drive motors is a big pain. If motor is off, you have to turn it
+ // on first. But you can not leave the motor on all the time, since that would wear out the
+ // disk. On the other hand, if you turn the motor off after each operation, the system performance
+ // will be awful. The compromise used in this driver is to leave the motor on for 2 seconds after
+ // each operation. If a new operation is started in that interval(2s), the motor need not be
+ // turned on again. If no new operation is started, a timer goes off and the motor is turned off.
+ //
+ DevPos = Info->DevPos;
+
+ //
+ // If the Motor is already on, just return EFI_SUCCESS.
+ //
+ if (Info->MotorOn) {
+ return EFI_SUCCESS;
+ }
+ //
+ // The drive's motor is off, so need turn it on.
+ // First check if command and drive are busy or not.
+ //
+ if (FdcWaitForBSYClear (FdcBlkIoDev, DevPos, 1) != EFI_SUCCESS) {
+ return EFI_DEVICE_ERROR;
+ }
+ //
+ // for drive A: 1CH, drive B: 2DH
+ //
+ Data = 0x0C;
+ Data = (UINT8) (Data | (SELECT_DRV & DevPos));
+ if (DevPos == 0) {
+ Data |= DRVA_MOTOR_ON;
+ } else {
+ Data |= DRVB_MOTOR_ON;
+ }
+
+ Info->MotorOn = FALSE;
+
+ //
+ // Turn on the motor and wait for some time to ensure it takes effect.
+ //
+ IoWrite8 ((UINT16) (PcdGet16 (PcdFdcBaseAddress) + FDC_REGISTER_DOR), Data);
+ MicroSecondDelay (FDC_LONG_DELAY);
+
+ Info->MotorOn = TRUE;
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Turn off the motor of floppy drive.
+
+ @param FdcBlkIoDev Instance of FDC_BLK_IO_DEV.
+ @param Info Information of floppy device.
+
+**/
+VOID
+MotorOff (
+ IN FDC_BLK_IO_DEV *FdcBlkIoDev,
+ IN OUT PEI_FLOPPY_DEVICE_INFO *Info
+ )
+{
+ UINT8 Data;
+ UINT8 DevPos;
+
+ DevPos = Info->DevPos;
+
+ if (!Info->MotorOn) {
+ return;
+ }
+ //
+ // The motor is on, so need motor off
+ //
+ Data = 0x0C;
+ Data = (UINT8) (Data | (SELECT_DRV & DevPos));
+
+ IoWrite8 ((UINT16) (PcdGet16 (PcdFdcBaseAddress) + FDC_REGISTER_DOR), Data);
+ MicroSecondDelay (FDC_SHORT_DELAY);
+
+ Info->MotorOn = FALSE;
+}
+
+/**
+ Recalibrate the FDC device.
+
+ @param FdcBlkIoDev Instance of FDC_BLK_IO_DEV.
+ @param Info Information of floppy device.
+
+ @retval EFI_SUCCESS FDC successfully recalibrated.
+ @retval EFI_DEVICE_ERROR Fail to send RECALIBRATE_CMD.
+ @retval EFI_DEVICE_ERROR Fail to get status value of Sts0 and Pcn.
+ @retval EFI_DEVICE_ERROR Fail to recalibrate FDC device.
+
+**/
+EFI_STATUS
+Recalibrate (
+ IN FDC_BLK_IO_DEV *FdcBlkIoDev,
+ IN OUT PEI_FLOPPY_DEVICE_INFO *Info
+ )
+{
+ FDC_COMMAND_PACKET2 Command;
+ UINTN Index;
+ UINT8 Sts0;
+ UINT8 Pcn;
+ UINT8 *Pointer;
+ UINT8 Count;
+ UINT8 DevPos;
+
+ DevPos = Info->DevPos;
+
+ //
+ // We would try twice.
+ //
+ Count = 2;
+ while (Count > 0) {
+ ZeroMem (&Command, sizeof (FDC_COMMAND_PACKET2));
+ Command.CommandCode = RECALIBRATE_CMD;
+ //
+ // drive select
+ //
+ if (DevPos == 0) {
+ Command.DiskHeadSel = 0;
+ } else {
+ Command.DiskHeadSel = 1;
+ }
+
+ Pointer = (UINT8 *) (&Command);
+ for (Index = 0; Index < sizeof (FDC_COMMAND_PACKET2); Index++) {
+ if (DataOutByte (FdcBlkIoDev, Pointer++) != EFI_SUCCESS) {
+ return EFI_DEVICE_ERROR;
+ }
+ }
+
+ MicroSecondDelay (FDC_RECALIBRATE_DELAY);
+
+ if (SenseIntStatus (FdcBlkIoDev, &Sts0, &Pcn) != EFI_SUCCESS) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ if ((Sts0 & 0xf0) == BIT5 && Pcn == 0) {
+ //
+ // Recalibration is successful.
+ //
+ Info->Pcn = 0;
+ Info->NeedRecalibrate = FALSE;
+
+ return EFI_SUCCESS;
+ } else {
+ //
+ // Recalibration is not successful. Try again.
+ // If trial is used out, return EFI_DEVICE_ERROR.
+ //
+ Count--;
+ if (Count == 0) {
+ return EFI_DEVICE_ERROR;
+ }
+ }
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Seek for the cylinder according to given LBA.
+
+ @param FdcBlkIoDev Instance of FDC_BLK_IO_DEV.
+ @param Info Information of floppy device.
+ @param Lba LBA for which to seek for cylinder.
+
+ @retval EFI_SUCCESS Successfully moved to the destination cylinder.
+ @retval EFI_SUCCESS Destination cylinder is just the present cylinder.
+ @retval EFI_DEVICE_ERROR Fail to move to the destination cylinder.
+
+**/
+EFI_STATUS
+Seek (
+ IN FDC_BLK_IO_DEV *FdcBlkIoDev,
+ IN OUT PEI_FLOPPY_DEVICE_INFO *Info,
+ IN EFI_PEI_LBA Lba
+ )
+{
+ FDC_SEEK_CMD Command;
+ DISKET_PARA_TABLE *Para;
+ UINT8 EndOfTrack;
+ UINT8 Head;
+ UINT8 Cylinder;
+ UINT8 Sts0;
+ UINT8 *Pointer;
+ UINT8 Pcn;
+ UINTN Index;
+ UINT8 Gap;
+ UINT8 DevPos;
+
+ DevPos = Info->DevPos;
+ if (Info->NeedRecalibrate) {
+ if (Recalibrate (FdcBlkIoDev, Info) != EFI_SUCCESS) {
+ return EFI_DEVICE_ERROR;
+ }
+ //
+ // Recalibrate Success
+ //
+ Info->NeedRecalibrate = FALSE;
+ }
+
+ //
+ // Get the base of disk parameter information corresponding to its type.
+ //
+ Para = (DISKET_PARA_TABLE *) ((UINT8 *) DiskPara + sizeof (DISKET_PARA_TABLE) * Info->Type);
+ EndOfTrack = Para->EndOfTrack;
+ //
+ // Calculate cylinder based on Lba and EOT
+ //
+ Cylinder = (UINT8) ((UINTN) Lba / EndOfTrack / 2);
+
+ //
+ // If the dest cylinder is the present cylinder, unnecessary to do the seek operation
+ //
+ if (Info->Pcn == Cylinder) {
+ return EFI_SUCCESS;
+ }
+
+ //
+ // Calculate the head : 0 or 1
+ //
+ Head = (UINT8) ((UINTN) Lba / EndOfTrack % 2);
+
+ ZeroMem (&Command, sizeof (FDC_SEEK_CMD));
+ Command.CommandCode = SEEK_CMD;
+ if (DevPos == 0) {
+ Command.DiskHeadSel = 0;
+ } else {
+ Command.DiskHeadSel = 1;
+ }
+
+ //
+ // Send command to move to destination cylinder.
+ //
+ Command.DiskHeadSel = (UINT8) (Command.DiskHeadSel | (Head << 2));
+ Command.NewCylinder = Cylinder;
+
+ Pointer = (UINT8 *) (&Command);
+ for (Index = 0; Index < sizeof (FDC_SEEK_CMD); Index++) {
+ if (DataOutByte (FdcBlkIoDev, Pointer++) != EFI_SUCCESS) {
+ return EFI_DEVICE_ERROR;
+ }
+ }
+
+ MicroSecondDelay (FDC_SHORT_DELAY);
+
+ //
+ // Calculate waiting time, which is proportional to the gap between destination
+ // cylinder and present cylinder.
+ //
+ if (Info->Pcn > Cylinder) {
+ Gap = (UINT8) (Info->Pcn - Cylinder);
+ } else {
+ Gap = (UINT8) (Cylinder - Info->Pcn);
+ }
+
+ MicroSecondDelay ((Gap + 1) * FDC_LONG_DELAY);
+
+ //
+ // Confirm if the new cylinder is the destination and status is correct.
+ //
+ if (SenseIntStatus (FdcBlkIoDev, &Sts0, &Pcn) != EFI_SUCCESS) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ if ((Sts0 & 0xf0) == BIT5) {
+ Info->Pcn = Command.NewCylinder;
+ Info->NeedRecalibrate = FALSE;
+ return EFI_SUCCESS;
+ } else {
+ Info->NeedRecalibrate = TRUE;
+ return EFI_DEVICE_ERROR;
+ }
+}
+
+/**
+ Check if diskette is changed.
+
+ @param FdcBlkIoDev Instance of FDC_BLK_IO_DEV
+ @param Info Information of floppy device.
+
+ @retval EFI_SUCCESS Diskette is not changed.
+ @retval EFI_MEDIA_CHANGED Diskette is changed.
+ @retval EFI_NO_MEDIA No diskette.
+ @retval EFI_DEVICE_ERROR Fail to do the seek or recalibrate operation.
+
+**/
+EFI_STATUS
+DisketChanged (
+ IN FDC_BLK_IO_DEV *FdcBlkIoDev,
+ IN OUT PEI_FLOPPY_DEVICE_INFO *Info
+ )
+{
+ EFI_STATUS Status;
+ UINT8 Data;
+
+ //
+ // Check change line
+ //
+ Data = IoRead8 ((UINT16) (PcdGet16 (PcdFdcBaseAddress) + FDC_REGISTER_DIR));
+
+ MicroSecondDelay (FDC_SHORT_DELAY);
+
+ if ((Data & DIR_DCL) == DIR_DCL) {
+ if (Info->Pcn != 0) {
+ Status = Recalibrate (FdcBlkIoDev, Info);
+ } else {
+ Status = Seek (FdcBlkIoDev, Info, 0x30);
+ }
+
+ if (Status != EFI_SUCCESS) {
+ //
+ // Fail to do the seek or recalibrate operation
+ //
+ return EFI_DEVICE_ERROR;
+ }
+
+ Data = IoRead8 ((UINT16) (PcdGet16 (PcdFdcBaseAddress) + FDC_REGISTER_DIR));
+
+ MicroSecondDelay (FDC_SHORT_DELAY);
+
+ if ((Data & DIR_DCL) == DIR_DCL) {
+ return EFI_NO_MEDIA;
+ }
+
+ return EFI_MEDIA_CHANGED;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Detects if FDC device exists.
+
+ @param FdcBlkIoDev Instance of FDC_BLK_IO_DEV
+ @param Info Information of floppy device.
+ @param MediaInfo Information of floppy media.
+
+ @retval TRUE FDC device exists and is working properly.
+ @retval FALSE FDC device does not exist or cannot work properly.
+
+**/
+BOOLEAN
+DiscoverFdcDevice (
+ IN FDC_BLK_IO_DEV *FdcBlkIoDev,
+ IN OUT PEI_FLOPPY_DEVICE_INFO *Info,
+ OUT EFI_PEI_BLOCK_IO_MEDIA *MediaInfo
+ )
+{
+ EFI_STATUS Status;
+ DISKET_PARA_TABLE *Para;
+
+ Status = MotorOn (FdcBlkIoDev, Info);
+ if (Status != EFI_SUCCESS) {
+ return FALSE;
+ }
+
+ Status = Recalibrate (FdcBlkIoDev, Info);
+
+ if (Status != EFI_SUCCESS) {
+ MotorOff (FdcBlkIoDev, Info);
+ return FALSE;
+ }
+ //
+ // Set Media Parameter
+ //
+ MediaInfo->DeviceType = LegacyFloppy;
+ MediaInfo->MediaPresent = TRUE;
+
+ //
+ // Check Media
+ //
+ Status = DisketChanged (FdcBlkIoDev, Info);
+ switch (Status) {
+ case EFI_NO_MEDIA:
+ //
+ // No diskette in floppy.
+ //
+ MediaInfo->MediaPresent = FALSE;
+ break;
+
+ case EFI_MEDIA_CHANGED:
+ case EFI_SUCCESS:
+ //
+ // Diskette exists in floppy.
+ //
+ break;
+
+ default:
+ //
+ // EFI_DEVICE_ERROR
+ //
+ MotorOff (FdcBlkIoDev, Info);
+ return FALSE;
+ }
+
+ MotorOff (FdcBlkIoDev, Info);
+
+ //
+ // Get the base of disk parameter information corresponding to its type.
+ //
+ Para = (DISKET_PARA_TABLE *) ((UINT8 *) DiskPara + sizeof (DISKET_PARA_TABLE) * Info->Type);
+
+ MediaInfo->BlockSize = BytePerSector[Para->Number];
+ MediaInfo->LastBlock = Para->EndOfTrack * 2 * (Para->MaxTrackNum + 1) - 1;
+
+ return TRUE;
+}
+
+/**
+ Enumerate floppy device
+
+ @param FdcBlkIoDev Instance of floppy device controller
+
+ @return Number of FDC devices.
+
+**/
+UINT8
+FdcEnumeration (
+ IN FDC_BLK_IO_DEV *FdcBlkIoDev
+ )
+{
+ UINT8 DevPos;
+ UINT8 DevNo;
+ EFI_PEI_BLOCK_IO_MEDIA MediaInfo;
+ EFI_STATUS Status;
+
+ DevNo = 0;
+
+ //
+ // DevPos=0 means Drive A, 1 means Drive B.
+ //
+ for (DevPos = 0; DevPos < 2; DevPos++) {
+ //
+ // Detecting device presence
+ //
+ REPORT_STATUS_CODE (EFI_PROGRESS_CODE, EFI_PERIPHERAL_REMOVABLE_MEDIA + EFI_P_PC_PRESENCE_DETECT);
+
+ //
+ // Reset FDC
+ //
+ Status = FdcReset (FdcBlkIoDev, DevPos);
+
+ if (EFI_ERROR (Status)) {
+ continue;
+ }
+
+ FdcBlkIoDev->DeviceInfo[DevPos].DevPos = DevPos;
+ FdcBlkIoDev->DeviceInfo[DevPos].Pcn = 0;
+ FdcBlkIoDev->DeviceInfo[DevPos].MotorOn = FALSE;
+ FdcBlkIoDev->DeviceInfo[DevPos].NeedRecalibrate = TRUE;
+ FdcBlkIoDev->DeviceInfo[DevPos].Type = FdcType1440K1440K;
+
+ //
+ // Discover FDC device
+ //
+ if (DiscoverFdcDevice (FdcBlkIoDev, &(FdcBlkIoDev->DeviceInfo[DevPos]), &MediaInfo)) {
+ FdcBlkIoDev->DeviceInfo[DevNo].DevPos = DevPos;
+
+ FdcBlkIoDev->DeviceInfo[DevNo].Pcn = FdcBlkIoDev->DeviceInfo[DevPos].Pcn;
+ FdcBlkIoDev->DeviceInfo[DevNo].MotorOn = FdcBlkIoDev->DeviceInfo[DevPos].MotorOn;
+ FdcBlkIoDev->DeviceInfo[DevNo].NeedRecalibrate = FdcBlkIoDev->DeviceInfo[DevPos].NeedRecalibrate;
+ FdcBlkIoDev->DeviceInfo[DevNo].Type = FdcBlkIoDev->DeviceInfo[DevPos].Type;
+
+ CopyMem (
+ &(FdcBlkIoDev->DeviceInfo[DevNo].MediaInfo),
+ &MediaInfo,
+ sizeof (EFI_PEI_BLOCK_IO_MEDIA)
+ );
+
+ DevNo++;
+ } else {
+ //
+ // Assume controller error
+ //
+ REPORT_STATUS_CODE (
+ EFI_ERROR_CODE | EFI_ERROR_MINOR,
+ EFI_PERIPHERAL_REMOVABLE_MEDIA + EFI_P_EC_CONTROLLER_ERROR
+ );
+ }
+ }
+
+ FdcBlkIoDev->DeviceCount = DevNo;
+ return DevNo;
+}
+
+/**
+ Checks result reflected by FDC_RESULT_PACKET.
+
+ @param Result FDC_RESULT_PACKET read from FDC after certain operation.
+ @param Info Information of floppy device.
+
+ @retval EFI_SUCCESS Result is healthy.
+ @retval EFI_DEVICE_ERROR Result is not healthy.
+
+**/
+EFI_STATUS
+CheckResult (
+ IN FDC_RESULT_PACKET *Result,
+ OUT PEI_FLOPPY_DEVICE_INFO *Info
+ )
+{
+ if ((Result->Status0 & STS0_IC) != IC_NT) {
+ if ((Result->Status0 & STS0_SE) == BIT5) {
+ //
+ // Seek error
+ //
+ Info->NeedRecalibrate = TRUE;
+ }
+
+ Info->NeedRecalibrate = TRUE;
+ return EFI_DEVICE_ERROR;
+ }
+ //
+ // Check Status Register1
+ //
+ if ((Result->Status1 & (STS1_EN | STS1_DE | STS1_OR | STS1_ND | STS1_NW | STS1_MA)) != 0) {
+ Info->NeedRecalibrate = TRUE;
+ return EFI_DEVICE_ERROR;
+ }
+ //
+ // Check Status Register2
+ //
+ if ((Result->Status2 & (STS2_CM | STS2_DD | STS2_WC | STS2_BC | STS2_MD)) != 0) {
+ Info->NeedRecalibrate = TRUE;
+ return EFI_DEVICE_ERROR;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Fill parameters for command packet.
+
+ @param Info Information of floppy device.
+ @param Lba Logical block address.
+ @param Command Command for which for fill parameters.
+
+**/
+VOID
+FillPara (
+ IN PEI_FLOPPY_DEVICE_INFO *Info,
+ IN EFI_PEI_LBA Lba,
+ OUT FDC_COMMAND_PACKET1 *Command
+ )
+{
+ DISKET_PARA_TABLE *Para;
+ UINT8 EndOfTrack;
+ UINT8 DevPos;
+
+ DevPos = Info->DevPos;
+
+ //
+ // Get the base of disk parameter information corresponding to its type.
+ //
+ Para = (DISKET_PARA_TABLE *) ((UINT8 *) DiskPara + sizeof (DISKET_PARA_TABLE) * Info->Type);
+
+ EndOfTrack = Para->EndOfTrack;
+
+ if (DevPos == 0) {
+ Command->DiskHeadSel = 0;
+ } else {
+ Command->DiskHeadSel = 1;
+ }
+
+ Command->Cylinder = (UINT8) ((UINTN) Lba / EndOfTrack / 2);
+ Command->Head = (UINT8) ((UINTN) Lba / EndOfTrack % 2);
+ Command->Sector = (UINT8) ((UINT8) ((UINTN) Lba % EndOfTrack) + 1);
+ Command->DiskHeadSel = (UINT8) (Command->DiskHeadSel | (Command->Head << 2));
+ Command->Number = Para->Number;
+ Command->EndOfTrack = Para->EndOfTrack;
+ Command->GapLength = Para->GapLength;
+ Command->DataLength = Para->DataLength;
+}
+
+/**
+ Setup specifed FDC device.
+
+ @param FdcBlkIoDev Instance of FDC_BLK_IO_DEV.
+ @param DevPos Index of FDC device.
+
+ @retval EFI_SUCCESS FDC device successfully set up.
+ @retval EFI_DEVICE_ERROR FDC device has errors.
+
+**/
+EFI_STATUS
+Setup (
+ IN FDC_BLK_IO_DEV *FdcBlkIoDev,
+ IN UINT8 DevPos
+ )
+{
+ EFI_STATUS Status;
+
+ IoWrite8 ((UINT16) (PcdGet16 (PcdFdcBaseAddress) + FDC_REGISTER_CCR), 0x0);
+
+ MicroSecondDelay (FDC_MEDIUM_DELAY);
+
+ Status = Specify (FdcBlkIoDev);
+ return Status;
+}
+
+/**
+ Setup DMA channels to read data.
+
+ @param FdcBlkIoDev Instance of FDC_BLK_IO_DEV.
+ @param Buffer Memory buffer for DMA transfer.
+ @param BlockSize the number of the bytes in one block.
+ @param NumberOfBlocks Number of blocks to read.
+
+**/
+VOID
+SetDMA (
+ IN FDC_BLK_IO_DEV *FdcBlkIoDev,
+ IN VOID *Buffer,
+ IN UINTN BlockSize,
+ IN UINTN NumberOfBlocks
+ )
+{
+ UINT8 Data;
+ UINTN Count;
+
+ //
+ // Mask DMA channel 2;
+ //
+ IoWrite8 (R_8237_DMA_WRSMSK_CH0_3, B_8237_DMA_WRSMSK_CMS | 2);
+
+ //
+ // Clear first/last flip flop
+ //
+ IoWrite8 (R_8237_DMA_CBPR_CH0_3, B_8237_DMA_WRSMSK_CMS | 2);
+
+ //
+ // Set mode
+ //
+ IoWrite8 (R_8237_DMA_CHMODE_CH0_3, V_8237_DMA_CHMODE_SINGLE | V_8237_DMA_CHMODE_IO2MEM | 2);
+
+ //
+ // Set base address and page register
+ //
+ Data = (UINT8) (UINTN) Buffer;
+ IoWrite8 (R_8237_DMA_BASE_CA_CH2, Data);
+ Data = (UINT8) ((UINTN) Buffer >> 8);
+ IoWrite8 (R_8237_DMA_BASE_CA_CH2, Data);
+
+ Data = (UINT8) ((UINTN) Buffer >> 16);
+ IoWrite8 (R_8237_DMA_MEM_LP_CH2, Data);
+
+ //
+ // Set count register
+ //
+ Count = BlockSize * NumberOfBlocks - 1;
+ Data = (UINT8) (Count & 0xff);
+ IoWrite8 (R_8237_DMA_BASE_CC_CH2, Data);
+ Data = (UINT8) (Count >> 8);
+ IoWrite8 (R_8237_DMA_BASE_CC_CH2, Data);
+
+ //
+ // Clear channel 2 mask
+ //
+ IoWrite8 (R_8237_DMA_WRSMSK_CH0_3, 0x02);
+}
+
+
+/**
+ According to the block range specified by Lba and NumberOfBlocks, calculate
+ the number of blocks in the same sector, which can be transferred in a batch.
+
+ @param Info Information of floppy device.
+ @param Lba Start address of block range.
+ @param NumberOfBlocks Number of blocks of the range.
+
+ @return Number of blocks in the same sector.
+
+**/
+UINTN
+GetTransferBlockCount (
+ IN PEI_FLOPPY_DEVICE_INFO *Info,
+ IN EFI_PEI_LBA Lba,
+ IN UINTN NumberOfBlocks
+ )
+{
+ DISKET_PARA_TABLE *Para;
+ UINT8 EndOfTrack;
+ UINT8 Head;
+ UINT8 SectorsInTrack;
+
+ //
+ // Get the base of disk parameter information corresponding to its type.
+ //
+ Para = (DISKET_PARA_TABLE *) ((UINT8 *) DiskPara + sizeof (DISKET_PARA_TABLE) * Info->Type);
+
+ EndOfTrack = Para->EndOfTrack;
+ Head = (UINT8) ((UINTN) Lba / EndOfTrack % 2);
+
+ SectorsInTrack = (UINT8) (EndOfTrack * (2 - Head) - (UINT8) ((UINTN) Lba % EndOfTrack));
+ if (SectorsInTrack < NumberOfBlocks) {
+ //
+ // Not all the block range locates in the same sector
+ //
+ return SectorsInTrack;
+ } else {
+ //
+ // All the block range is in the same sector.
+ //
+ return NumberOfBlocks;
+ }
+}
+
+/**
+ Read data sector from FDC device.
+
+ @param FdcBlkIoDev Instance of FDC_BLK_IO_DEV.
+ @param Info Information of floppy device.
+ @param Buffer Buffer to setup for DMA.
+ @param Lba The start address to read.
+ @param NumberOfBlocks Number of blocks to read.
+
+ @retval EFI_SUCCESS Data successfully read out.
+ @retval EFI_DEVICE_ERROR FDC device has errors.
+ @retval EFI_TIMEOUT Command does not take effect in time.
+
+**/
+EFI_STATUS
+ReadDataSector (
+ IN FDC_BLK_IO_DEV *FdcBlkIoDev,
+ IN OUT PEI_FLOPPY_DEVICE_INFO *Info,
+ IN VOID *Buffer,
+ IN EFI_PEI_LBA Lba,
+ IN UINTN NumberOfBlocks
+ )
+{
+ EFI_STATUS Status;
+ FDC_COMMAND_PACKET1 Command;
+ FDC_RESULT_PACKET Result;
+ UINTN Index;
+ UINTN Times;
+ UINT8 *Pointer;
+
+ Status = Seek (FdcBlkIoDev, Info, Lba);
+ if (Status != EFI_SUCCESS) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ //
+ // Set up DMA
+ //
+ SetDMA (FdcBlkIoDev, Buffer, Info->MediaInfo.BlockSize, NumberOfBlocks);
+
+ //
+ // Allocate Read command packet
+ //
+ ZeroMem (&Command, sizeof (FDC_COMMAND_PACKET1));
+ Command.CommandCode = READ_DATA_CMD | CMD_MT | CMD_MFM | CMD_SK;
+
+ //
+ // Fill parameters for command.
+ //
+ FillPara (Info, Lba, &Command);
+
+ //
+ // Write command bytes to FDC
+ //
+ Pointer = (UINT8 *) (&Command);
+ for (Index = 0; Index < sizeof (FDC_COMMAND_PACKET1); Index++) {
+ if (DataOutByte (FdcBlkIoDev, Pointer++) != EFI_SUCCESS) {
+ return EFI_DEVICE_ERROR;
+ }
+ }
+
+ //
+ // Wait for some time until command takes effect.
+ //
+ Times = (STALL_1_SECOND / FDC_CHECK_INTERVAL) + 1;
+ do {
+ if ((IoRead8 ((UINT16) (PcdGet16 (PcdFdcBaseAddress) + FDC_REGISTER_MSR)) & 0xc0) == 0xc0) {
+ break;
+ }
+
+ MicroSecondDelay (FDC_SHORT_DELAY);
+ } while (--Times > 0);
+
+ if (Times == 0) {
+ //
+ // Command fails to take effect in time, return EFI_TIMEOUT.
+ //
+ return EFI_TIMEOUT;
+ }
+
+ //
+ // Read result bytes from FDC
+ //
+ Pointer = (UINT8 *) (&Result);
+ for (Index = 0; Index < sizeof (FDC_RESULT_PACKET); Index++) {
+ if (DataInByte (FdcBlkIoDev, Pointer++) != EFI_SUCCESS) {
+ return EFI_DEVICE_ERROR;
+ }
+ }
+
+ return CheckResult (&Result, Info);
+}
+
+/**
+ Gets the count of block I/O devices that one specific block driver detects.
+
+ This function is used for getting the count of block I/O devices that one
+ specific block driver detects. To the PEI ATAPI driver, it returns the number
+ of all the detected ATAPI devices it detects during the enumeration process.
+ To the PEI legacy floppy driver, it returns the number of all the legacy
+ devices it finds during its enumeration process. If no device is detected,
+ then the function will return zero.
+
+ @param[in] PeiServices General-purpose services that are available
+ to every PEIM.
+ @param[in] This Indicates the EFI_PEI_RECOVERY_BLOCK_IO_PPI
+ instance.
+ @param[out] NumberBlockDevices The number of block I/O devices discovered.
+
+ @retval EFI_SUCCESS Operation performed successfully.
+
+**/
+EFI_STATUS
+EFIAPI
+FdcGetNumberOfBlockDevices (
+ IN EFI_PEI_SERVICES **PeiServices,
+ IN EFI_PEI_RECOVERY_BLOCK_IO_PPI *This,
+ OUT UINTN *NumberBlockDevices
+ )
+{
+ FDC_BLK_IO_DEV *FdcBlkIoDev;
+
+ FdcBlkIoDev = NULL;
+
+ FdcBlkIoDev = PEI_RECOVERY_FDC_FROM_BLKIO_THIS (This);
+
+ *NumberBlockDevices = FdcBlkIoDev->DeviceCount;
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Gets a block device's media information.
+
+ This function will provide the caller with the specified block device's media
+ information. If the media changes, calling this function will update the media
+ information accordingly.
+
+ @param[in] PeiServices General-purpose services that are available to every
+ PEIM
+ @param[in] This Indicates the EFI_PEI_RECOVERY_BLOCK_IO_PPI instance.
+ @param[in] DeviceIndex Specifies the block device to which the function wants
+ to talk. Because the driver that implements Block I/O
+ PPIs will manage multiple block devices, the PPIs that
+ want to talk to a single device must specify the
+ device index that was assigned during the enumeration
+ process. This index is a number from one to
+ NumberBlockDevices.
+ @param[out] MediaInfo The media information of the specified block media.
+ The caller is responsible for the ownership of this
+ data structure.
+
+ @retval EFI_SUCCESS Media information about the specified block device
+ was obtained successfully.
+ @retval EFI_DEVICE_ERROR Cannot get the media information due to a hardware
+ error.
+ @retval Others Other failure occurs.
+
+**/
+EFI_STATUS
+EFIAPI
+FdcGetBlockDeviceMediaInfo (
+ IN EFI_PEI_SERVICES **PeiServices,
+ IN EFI_PEI_RECOVERY_BLOCK_IO_PPI *This,
+ IN UINTN DeviceIndex,
+ OUT EFI_PEI_BLOCK_IO_MEDIA *MediaInfo
+ )
+{
+ UINTN DeviceCount;
+ FDC_BLK_IO_DEV *FdcBlkIoDev;
+ BOOLEAN Healthy;
+ UINTN Index;
+
+ FdcBlkIoDev = NULL;
+
+ if (This == NULL || MediaInfo == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ FdcBlkIoDev = PEI_RECOVERY_FDC_FROM_BLKIO_THIS (This);
+
+ DeviceCount = FdcBlkIoDev->DeviceCount;
+
+ //
+ // DeviceIndex is a value from 1 to NumberBlockDevices.
+ //
+ if ((DeviceIndex < 1) || (DeviceIndex > DeviceCount) || (DeviceIndex > 2)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Index = DeviceIndex - 1;
+ //
+ // Probe media and retrieve latest media information
+ //
+ Healthy = DiscoverFdcDevice (
+ FdcBlkIoDev,
+ &FdcBlkIoDev->DeviceInfo[Index],
+ MediaInfo
+ );
+
+ if (!Healthy) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ CopyMem (
+ &(FdcBlkIoDev->DeviceInfo[Index].MediaInfo),
+ MediaInfo,
+ sizeof (EFI_PEI_BLOCK_IO_MEDIA)
+ );
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Reads the requested number of blocks from the specified block device.
+
+ The function reads the requested number of blocks from the device. All the
+ blocks are read, or an error is returned. If there is no media in the device,
+ the function returns EFI_NO_MEDIA.
+
+ @param[in] PeiServices General-purpose services that are available to
+ every PEIM.
+ @param[in] This Indicates the EFI_PEI_RECOVERY_BLOCK_IO_PPI instance.
+ @param[in] DeviceIndex Specifies the block device to which the function wants
+ to talk. Because the driver that implements Block I/O
+ PPIs will manage multiple block devices, the PPIs that
+ want to talk to a single device must specify the device
+ index that was assigned during the enumeration process.
+ This index is a number from one to NumberBlockDevices.
+ @param[in] StartLBA The starting logical block address (LBA) to read from
+ on the device
+ @param[in] BufferSize The size of the Buffer in bytes. This number must be
+ a multiple of the intrinsic block size of the device.
+ @param[out] Buffer A pointer to the destination buffer for the data.
+ The caller is responsible for the ownership of the
+ buffer.
+
+ @retval EFI_SUCCESS The data was read correctly from the device.
+ @retval EFI_DEVICE_ERROR The device reported an error while attempting
+ to perform the read operation.
+ @retval EFI_INVALID_PARAMETER The read request contains LBAs that are not
+ valid, or the buffer is not properly aligned.
+ @retval EFI_NO_MEDIA There is no media in the device.
+ @retval EFI_BAD_BUFFER_SIZE The BufferSize parameter is not a multiple of
+ the intrinsic block size of the device.
+
+**/
+EFI_STATUS
+EFIAPI
+FdcReadBlocks (
+ IN EFI_PEI_SERVICES **PeiServices,
+ IN EFI_PEI_RECOVERY_BLOCK_IO_PPI *This,
+ IN UINTN DeviceIndex,
+ IN EFI_PEI_LBA StartLBA,
+ IN UINTN BufferSize,
+ OUT VOID *Buffer
+ )
+{
+ EFI_PEI_BLOCK_IO_MEDIA MediaInfo;
+ EFI_STATUS Status;
+ UINTN Count;
+ UINTN NumberOfBlocks;
+ UINTN BlockSize;
+ FDC_BLK_IO_DEV *FdcBlkIoDev;
+ VOID *MemPage;
+
+ FdcBlkIoDev = NULL;
+
+ if (This == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ FdcBlkIoDev = PEI_RECOVERY_FDC_FROM_BLKIO_THIS (This);
+
+ if (Buffer == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Status = FdcGetBlockDeviceMediaInfo (PeiServices, This, DeviceIndex, &MediaInfo);
+ if (Status != EFI_SUCCESS) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ if (!MediaInfo.MediaPresent) {
+ return EFI_NO_MEDIA;
+ }
+
+ BlockSize = MediaInfo.BlockSize;
+
+ //
+ // If BufferSize cannot be divided by block size of FDC device,
+ // return EFI_BAD_BUFFER_SIZE.
+ //
+ if (BufferSize % BlockSize != 0) {
+ return EFI_BAD_BUFFER_SIZE;
+ }
+
+ NumberOfBlocks = BufferSize / BlockSize;
+
+ if ((StartLBA + NumberOfBlocks - 1) > FdcBlkIoDev->DeviceInfo[DeviceIndex - 1].MediaInfo.LastBlock) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ MemPage = AllocatePages (EFI_SIZE_TO_PAGES (BufferSize));
+ if ((MemPage == NULL) || ((UINTN) MemPage >= ISA_MAX_MEMORY_ADDRESS)) {
+ //
+ // If fail to allocate memory under ISA_MAX_MEMORY_ADDRESS, designate the address space for DMA
+ //
+ MemPage = (VOID *) ((UINTN) (UINT32) 0x0f00000);
+ }
+ Status = MotorOn (FdcBlkIoDev, &(FdcBlkIoDev->DeviceInfo[DeviceIndex - 1]));
+ if (Status != EFI_SUCCESS) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ Status = Setup (FdcBlkIoDev, FdcBlkIoDev->DeviceInfo[DeviceIndex - 1].DevPos);
+ if (Status != EFI_SUCCESS) {
+ MotorOff (FdcBlkIoDev, &(FdcBlkIoDev->DeviceInfo[DeviceIndex - 1]));
+ return EFI_DEVICE_ERROR;
+ }
+ //
+ // Read data in batches.
+ // Blocks in the same cylinder are read out in a batch.
+ //
+ while ((Count = GetTransferBlockCount (
+ &(FdcBlkIoDev->DeviceInfo[DeviceIndex - 1]),
+ StartLBA,
+ NumberOfBlocks
+ )) != 0 && Status == EFI_SUCCESS) {
+ Status = ReadDataSector (
+ FdcBlkIoDev,
+ &(FdcBlkIoDev->DeviceInfo[DeviceIndex - 1]),
+ MemPage,
+ StartLBA,
+ Count
+ );
+ CopyMem (Buffer, MemPage, BlockSize * Count);
+ StartLBA += Count;
+ NumberOfBlocks -= Count;
+ Buffer = (VOID *) ((UINTN) Buffer + Count * BlockSize);
+ }
+
+ MotorOff (FdcBlkIoDev, &(FdcBlkIoDev->DeviceInfo[DeviceIndex - 1]));
+
+ switch (Status) {
+ case EFI_SUCCESS:
+ return EFI_SUCCESS;
+
+ default:
+ FdcReset (FdcBlkIoDev, FdcBlkIoDev->DeviceInfo[DeviceIndex - 1].DevPos);
+ return EFI_DEVICE_ERROR;
+ }
+}
+
+/**
+ Initializes the floppy disk controller and installs FDC Block I/O PPI.
+
+ @param FileHandle Handle of the file being invoked.
+ @param PeiServices Describes the list of possible PEI Services.
+
+ @retval EFI_SUCCESS Successfully initialized FDC and installed PPI.
+ @retval EFI_NOT_FOUND Cannot find FDC device.
+ @retval EFI_OUT_OF_RESOURCES Have no enough memory to create instance or descriptors.
+ @retval Other Fail to install FDC Block I/O PPI.
+
+**/
+EFI_STATUS
+EFIAPI
+FdcPeimEntry (
+ IN EFI_PEI_FILE_HANDLE FileHandle,
+ IN CONST EFI_PEI_SERVICES **PeiServices
+ )
+{
+ EFI_STATUS Status;
+ FDC_BLK_IO_DEV *FdcBlkIoDev;
+ UINTN DeviceCount;
+ UINT32 Index;
+
+ Status = PeiServicesRegisterForShadow (FileHandle);
+ if (!EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Allocate memory for instance of FDC_BLK_IO_DEV and copy initial value
+ // from template to it.
+ //
+ FdcBlkIoDev = AllocatePages (EFI_SIZE_TO_PAGES(sizeof (FDC_BLK_IO_DEV)));
+ if (FdcBlkIoDev == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+ CopyMem (FdcBlkIoDev, &mBlockIoDevTemplate, sizeof (mBlockIoDevTemplate));
+
+ //
+ // Initialize DMA controller to enable all channels.
+ //
+ for (Index = 0; Index < sizeof (mRegisterTable) / sizeof (PEI_DMA_TABLE); Index++) {
+ IoWrite8 (mRegisterTable[Index].Register, mRegisterTable[Index].Value);
+ }
+ REPORT_STATUS_CODE (EFI_PROGRESS_CODE, EFI_PERIPHERAL_REMOVABLE_MEDIA + EFI_P_PC_INIT);
+
+ //
+ // Enumerate FDC devices.
+ //
+ DeviceCount = FdcEnumeration (FdcBlkIoDev);
+ if (DeviceCount == 0) {
+ return EFI_NOT_FOUND;
+ }
+
+ FdcBlkIoDev->PpiDescriptor.Ppi = &FdcBlkIoDev->FdcBlkIo;
+
+ return PeiServicesInstallPpi (&FdcBlkIoDev->PpiDescriptor);
+}
diff --git a/IntelFrameworkModulePkg/Bus/Isa/IsaFloppyPei/FloppyPeim.h b/IntelFrameworkModulePkg/Bus/Isa/IsaFloppyPei/FloppyPeim.h
new file mode 100644
index 0000000000..6477394efc
--- /dev/null
+++ b/IntelFrameworkModulePkg/Bus/Isa/IsaFloppyPei/FloppyPeim.h
@@ -0,0 +1,246 @@
+/** @file
+Private include file for IsaFloppyPei PEIM.
+
+Copyright (c) 2006 - 2010, Intel Corporation. All rights reserved.<BR>
+
+This program and the accompanying materials
+are licensed and made available under the terms and conditions
+of the BSD License which accompanies this distribution. The
+full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef _RECOVERY_FLOPPY_H_
+#define _RECOVERY_FLOPPY_H_
+
+#include <Ppi/BlockIo.h>
+
+#include <Library/DebugLib.h>
+#include <Library/PeimEntryPoint.h>
+#include <Library/PeiServicesLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/ReportStatusCodeLib.h>
+#include <Library/TimerLib.h>
+#include <Library/IoLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/PcdLib.h>
+
+#include "Fdc.h"
+
+
+//
+// Some PC AT Compatible Device definitions
+//
+//
+// 8237 DMA registers
+//
+#define R_8237_DMA_BASE_CA_CH0 0x00
+#define R_8237_DMA_BASE_CA_CH1 0x02
+#define R_8237_DMA_BASE_CA_CH2 0x04
+#define R_8237_DMA_BASE_CA_CH3 0xd6
+#define R_8237_DMA_BASE_CA_CH5 0xc4
+#define R_8237_DMA_BASE_CA_CH6 0xc8
+#define R_8237_DMA_BASE_CA_CH7 0xcc
+
+#define R_8237_DMA_BASE_CC_CH0 0x01
+#define R_8237_DMA_BASE_CC_CH1 0x03
+#define R_8237_DMA_BASE_CC_CH2 0x05
+#define R_8237_DMA_BASE_CC_CH3 0xd7
+#define R_8237_DMA_BASE_CC_CH5 0xc6
+#define R_8237_DMA_BASE_CC_CH6 0xca
+#define R_8237_DMA_BASE_CC_CH7 0xce
+
+#define R_8237_DMA_MEM_LP_CH0 0x87
+#define R_8237_DMA_MEM_LP_CH1 0x83
+#define R_8237_DMA_MEM_LP_CH2 0x81
+#define R_8237_DMA_MEM_LP_CH3 0x82
+#define R_8237_DMA_MEM_LP_CH5 0x8B
+#define R_8237_DMA_MEM_LP_CH6 0x89
+#define R_8237_DMA_MEM_LP_CH7 0x8A
+
+
+#define R_8237_DMA_COMMAND_CH0_3 0x08
+#define R_8237_DMA_COMMAND_CH4_7 0xd0
+#define B_8237_DMA_COMMAND_GAP 0x10
+#define B_8237_DMA_COMMAND_CGE 0x04
+
+
+#define R_8237_DMA_STA_CH0_3 0x09
+#define R_8237_DMA_STA_CH4_7 0xd2
+
+#define R_8237_DMA_WRSMSK_CH0_3 0x0a
+#define R_8237_DMA_WRSMSK_CH4_7 0xd4
+#define B_8237_DMA_WRSMSK_CMS 0x04
+
+
+#define R_8237_DMA_CHMODE_CH0_3 0x0b
+#define R_8237_DMA_CHMODE_CH4_7 0xd6
+#define V_8237_DMA_CHMODE_DEMAND 0x00
+#define V_8237_DMA_CHMODE_SINGLE 0x40
+#define V_8237_DMA_CHMODE_CASCADE 0xc0
+#define B_8237_DMA_CHMODE_DECREMENT 0x20
+#define B_8237_DMA_CHMODE_INCREMENT 0x00
+#define B_8237_DMA_CHMODE_AE 0x10
+#define V_8237_DMA_CHMODE_VERIFY 0
+#define V_8237_DMA_CHMODE_IO2MEM 0x04
+#define V_8237_DMA_CHMODE_MEM2IO 0x08
+
+#define R_8237_DMA_CBPR_CH0_3 0x0c
+#define R_8237_DMA_CBPR_CH4_7 0xd8
+
+#define R_8237_DMA_MCR_CH0_3 0x0d
+#define R_8237_DMA_MCR_CH4_7 0xda
+
+#define R_8237_DMA_CLMSK_CH0_3 0x0e
+#define R_8237_DMA_CLMSK_CH4_7 0xdc
+
+#define R_8237_DMA_WRMSK_CH0_3 0x0f
+#define R_8237_DMA_WRMSK_CH4_7 0xde
+
+///
+/// ISA memory range
+///
+#define ISA_MAX_MEMORY_ADDRESS 0x1000000
+
+//
+// Macro for time delay & interval
+//
+#define STALL_1_SECOND 1000000
+#define STALL_1_MSECOND 1000
+#define FDC_CHECK_INTERVAL 50
+
+#define FDC_SHORT_DELAY 50
+#define FDC_MEDIUM_DELAY 100
+#define FDC_LONG_DELAY 4000
+#define FDC_RESET_DELAY 2000
+#define FDC_RECALIBRATE_DELAY 250000
+
+typedef enum {
+ FdcType360K360K = 0,
+ FdcType360K1200K,
+ FdcType1200K1200K,
+ FdcType720K720K,
+ FdcType720K1440K,
+ FdcType1440K1440K,
+ FdcType720K2880K,
+ FdcType1440K2880K,
+ FdcType2880K2880K
+} FDC_DISKET_TYPE;
+
+typedef struct {
+ UINT8 Register;
+ UINT8 Value;
+} PEI_DMA_TABLE;
+
+typedef struct {
+ UINT8 DevPos;
+ UINT8 Pcn;
+ BOOLEAN MotorOn;
+ BOOLEAN NeedRecalibrate;
+ FDC_DISKET_TYPE Type;
+ EFI_PEI_BLOCK_IO_MEDIA MediaInfo;
+} PEI_FLOPPY_DEVICE_INFO;
+
+#define FDC_BLK_IO_DEV_SIGNATURE SIGNATURE_32 ('F', 'b', 'i', 'o')
+
+typedef struct {
+ UINTN Signature;
+ EFI_PEI_RECOVERY_BLOCK_IO_PPI FdcBlkIo;
+ EFI_PEI_PPI_DESCRIPTOR PpiDescriptor;
+ UINTN DeviceCount;
+ PEI_FLOPPY_DEVICE_INFO DeviceInfo[2];
+} FDC_BLK_IO_DEV;
+
+#define PEI_RECOVERY_FDC_FROM_BLKIO_THIS(a) CR (a, FDC_BLK_IO_DEV, FdcBlkIo, FDC_BLK_IO_DEV_SIGNATURE)
+
+//
+// PEI Recovery Block I/O PPI
+//
+
+/**
+ Get the number of FDC devices.
+
+ This function implements EFI_PEI_RECOVERY_BLOCK_IO_PPI.GetNumberOfBlockDevices.
+ It get the number of FDC devices in the system.
+
+ @param PeiServices An indirect pointer to the PEI Services Table published by the PEI Foundation.
+ @param This Pointer to this PPI instance.
+ @param NumberBlockDevices Pointer to the the number of FDC devices for output.
+
+ @retval EFI_SUCCESS Number of FDC devices is retrieved successfully.
+ @retval EFI_INVALID_PARAMETER Parameter This is NULL.
+
+**/
+EFI_STATUS
+EFIAPI
+FdcGetNumberOfBlockDevices (
+ IN EFI_PEI_SERVICES **PeiServices,
+ IN EFI_PEI_RECOVERY_BLOCK_IO_PPI *This,
+ OUT UINTN *NumberBlockDevices
+ );
+
+/**
+ Get the specified media information.
+
+ This function implements EFI_PEI_RECOVERY_BLOCK_IO_PPI.GetBlockDeviceMediaInfo.
+ It gets the specified media information.
+
+ @param PeiServices An indirect pointer to the PEI Services Table published by the PEI Foundation.
+ @param This Pointer to this PPI instance.
+ @param DeviceIndex Index of FDC device to get information.
+ @param MediaInfo Pointer to the media info buffer for output.
+
+ @retval EFI_SUCCESS Number of FDC devices is retrieved successfully.
+ @retval EFI_INVALID_PARAMETER Parameter This is NULL.
+ @retval EFI_INVALID_PARAMETER Parameter MediaInfo is NULL.
+ @retval EFI_INVALID_PARAMETER DeviceIndex is not valid.
+ @retval EFI_DEVICE_ERROR FDC device does not exist or has errors.
+
+**/
+EFI_STATUS
+EFIAPI
+FdcGetBlockDeviceMediaInfo (
+ IN EFI_PEI_SERVICES **PeiServices,
+ IN EFI_PEI_RECOVERY_BLOCK_IO_PPI *This,
+ IN UINTN DeviceIndex,
+ OUT EFI_PEI_BLOCK_IO_MEDIA *MediaInfo
+ );
+
+/**
+ Get the requested number of blocks from the specified FDC device.
+
+ This function implements EFI_PEI_RECOVERY_BLOCK_IO_PPI.ReadBlocks.
+ It reads the requested number of blocks from the specified FDC device.
+
+ @param PeiServices An indirect pointer to the PEI Services Table published by the PEI Foundation.
+ @param This Pointer to this PPI instance.
+ @param DeviceIndex Index of FDC device to get information.
+ @param StartLba The start LBA to read from.
+ @param BufferSize The size of range to read.
+ @param Buffer Buffer to hold the data read from FDC.
+
+ @retval EFI_SUCCESS Number of FDC devices is retrieved successfully.
+ @retval EFI_INVALID_PARAMETER Parameter This is NULL.
+ @retval EFI_INVALID_PARAMETER Parameter Buffer is NULL.
+ @retval EFI_INVALID_PARAMETER Parameter BufferSize cannot be divided by block size of FDC device.
+ @retval EFI_NO_MEDIA No media present.
+ @retval EFI_DEVICE_ERROR FDC device has error.
+ @retval Others Fail to read blocks.
+
+**/
+EFI_STATUS
+EFIAPI
+FdcReadBlocks (
+ IN EFI_PEI_SERVICES **PeiServices,
+ IN EFI_PEI_RECOVERY_BLOCK_IO_PPI *This,
+ IN UINTN DeviceIndex,
+ IN EFI_PEI_LBA StartLba,
+ IN UINTN BufferSize,
+ OUT VOID *Buffer
+ );
+
+#endif
diff --git a/IntelFrameworkModulePkg/Bus/Isa/IsaFloppyPei/IsaFloppyPei.inf b/IntelFrameworkModulePkg/Bus/Isa/IsaFloppyPei/IsaFloppyPei.inf
new file mode 100644
index 0000000000..2e2c4f3574
--- /dev/null
+++ b/IntelFrameworkModulePkg/Bus/Isa/IsaFloppyPei/IsaFloppyPei.inf
@@ -0,0 +1,68 @@
+## @file
+# ISA Floppy PEIM to support recovery boot via floppy disk.
+#
+# This module detects Floppy devices. If found, it will install BlockIo PPI.
+# This module is only dispatched in Recovery Boot mode.
+#
+# Copyright (c) 2006 - 2011, Intel Corporation. All rights reserved.<BR>
+#
+# This program and the accompanying materials
+# are licensed and made available under the terms and conditions
+# of the BSD License which accompanies this distribution. The
+# full text of the license may be found at
+# http://opensource.org/licenses/bsd-license.php
+#
+# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+#
+##
+
+################################################################################
+#
+# Defines Section - statements that will be processed to create a Makefile.
+#
+################################################################################
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = IsaFloppyPei
+ FILE_GUID = 7F6E0A24-DBFD-43df-9755-0292D7D3DD48
+ MODULE_TYPE = PEIM
+ VERSION_STRING = 1.0
+
+ ENTRY_POINT = FdcPeimEntry
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+# VALID_ARCHITECTURES = IA32 X64 IPF EBC (EBC is for build only)
+#
+
+[Sources]
+ FloppyPeim.c
+ FloppyPeim.h
+ Fdc.h
+
+[Packages]
+ IntelFrameworkModulePkg/IntelFrameworkModulePkg.dec
+ MdePkg/MdePkg.dec
+
+[LibraryClasses]
+ IoLib
+ TimerLib
+ ReportStatusCodeLib
+ BaseMemoryLib
+ PeiServicesLib
+ PeimEntryPoint
+ DebugLib
+ MemoryAllocationLib
+ PcdLib
+
+[Ppis]
+ gEfiPeiVirtualBlockIoPpiGuid # PPI ALWAYS_PRODUCED
+
+[Pcd]
+ gEfiIntelFrameworkModulePkgTokenSpaceGuid.PcdFdcBaseAddress
+
+[Depex]
+ gEfiPeiMemoryDiscoveredPpiGuid AND gEfiPeiBootInRecoveryModePpiGuid
+