mirror of
				https://git.proxmox.com/git/mirror_edk2
				synced 2025-11-04 11:29:11 +00:00 
			
		
		
		
	Signed-off-by: Liming Gao <liming.gao@intel.com> Reviewed-by: Star Zeng <star.zeng@intel.com> git-svn-id: https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2@14352 6f19259b-4bc3-4df7-8a09-765794883524
		
			
				
	
	
		
			1759 lines
		
	
	
		
			44 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1759 lines
		
	
	
		
			44 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/** @file
 | 
						|
Floppy Peim to support Recovery function from Floppy device.
 | 
						|
 | 
						|
Copyright (c) 2006 - 2013, 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);
 | 
						|
  if (Status == EFI_NO_MEDIA) {
 | 
						|
    //
 | 
						|
    // No diskette in floppy.
 | 
						|
    //
 | 
						|
    MediaInfo->MediaPresent = FALSE;    
 | 
						|
  } else if (Status != EFI_MEDIA_CHANGED && Status != EFI_SUCCESS) {
 | 
						|
    //
 | 
						|
    // 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);
 | 
						|
}
 |