Home | History | Annotate | Download | only in IsaFloppyPei
      1 /** @file
      2 Floppy Peim to support Recovery function from Floppy device.
      3 
      4 Copyright (c) 2006 - 2014, Intel Corporation. All rights reserved.<BR>
      5 
      6 This program and the accompanying materials
      7 are licensed and made available under the terms and conditions
      8 of the BSD License which accompanies this distribution.  The
      9 full text of the license may be found at
     10 http://opensource.org/licenses/bsd-license.php
     11 
     12 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
     13 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
     14 
     15 **/
     16 
     17 
     18 #include "FloppyPeim.h"
     19 
     20 
     21 PEI_DMA_TABLE      mRegisterTable[] = {
     22   //
     23   // DMA2: Clear Byte Ptr, Enable
     24   //
     25   {
     26     R_8237_DMA_CBPR_CH4_7,
     27     0
     28   },
     29   {
     30     R_8237_DMA_COMMAND_CH4_7,
     31     0
     32   },
     33   //
     34   // DMA1: Clear Byte Ptr, Enable
     35   //
     36   {
     37     R_8237_DMA_CBPR_CH0_3,
     38     0
     39   },
     40   {
     41     R_8237_DMA_COMMAND_CH0_3,
     42     0
     43   },
     44   //
     45   // Configure Channel 4 for Cascade Mode
     46   // Clear DMA Request and enable DREQ
     47   //
     48   {
     49     R_8237_DMA_CHMODE_CH4_7,
     50     V_8237_DMA_CHMODE_CASCADE | 0
     51   },
     52   {
     53     R_8237_DMA_STA_CH4_7,
     54     0
     55   },
     56   {
     57     R_8237_DMA_WRSMSK_CH4_7,
     58     0
     59   },
     60   //
     61   // Configure DMA1 (Channels 0-3) for Single Mode
     62   // Clear DMA Request and enable DREQ
     63   //
     64   {
     65     R_8237_DMA_CHMODE_CH0_3,
     66     V_8237_DMA_CHMODE_SINGLE | 0
     67   },
     68   {
     69     R_8237_DMA_STA_CH0_3,
     70     0
     71   },
     72   {
     73     R_8237_DMA_WRSMSK_CH0_3,
     74     0
     75   },
     76   {
     77     R_8237_DMA_CHMODE_CH0_3,
     78     V_8237_DMA_CHMODE_SINGLE | 1
     79   },
     80   {
     81     R_8237_DMA_STA_CH0_3,
     82     1
     83   },
     84   {
     85     R_8237_DMA_WRSMSK_CH0_3,
     86     1
     87   },
     88   {
     89     R_8237_DMA_CHMODE_CH0_3,
     90     V_8237_DMA_CHMODE_SINGLE | 2
     91   },
     92   {
     93     R_8237_DMA_STA_CH0_3,
     94     2
     95   },
     96   {
     97     R_8237_DMA_WRSMSK_CH0_3,
     98     2
     99   },
    100   {
    101     R_8237_DMA_CHMODE_CH0_3,
    102     V_8237_DMA_CHMODE_SINGLE | 3
    103   },
    104   {
    105     R_8237_DMA_STA_CH0_3,
    106     3
    107   },
    108   {
    109     R_8237_DMA_WRSMSK_CH0_3,
    110     3
    111   },
    112   //
    113   // Configure DMA2 (Channels 5-7) for Single Mode
    114   // Clear DMA Request and enable DREQ
    115   //
    116   {
    117     R_8237_DMA_CHMODE_CH4_7,
    118     V_8237_DMA_CHMODE_SINGLE | 1
    119   },
    120   {
    121     R_8237_DMA_STA_CH4_7,
    122     1
    123   },
    124   {
    125     R_8237_DMA_WRSMSK_CH4_7,
    126     1
    127   },
    128   {
    129     R_8237_DMA_CHMODE_CH4_7,
    130     V_8237_DMA_CHMODE_SINGLE | 2
    131   },
    132   {
    133     R_8237_DMA_STA_CH4_7,
    134     2
    135   },
    136   {
    137     R_8237_DMA_WRSMSK_CH4_7,
    138     2
    139   },
    140   {
    141     R_8237_DMA_CHMODE_CH4_7,
    142     V_8237_DMA_CHMODE_SINGLE | 3
    143   },
    144   {
    145     R_8237_DMA_STA_CH4_7,
    146     3
    147   },
    148   {
    149     R_8237_DMA_WRSMSK_CH4_7,
    150     3
    151   }
    152 };
    153 
    154 //
    155 // Table of diskette parameters of various diskette types
    156 //
    157 DISKET_PARA_TABLE  DiskPara[9] = {
    158   {
    159     0x09,
    160     0x50,
    161     0xff,
    162     0x2,
    163     0x27,
    164     0x4,
    165     0x25,
    166     0x14,
    167     0x80
    168   },
    169   {
    170     0x09,
    171     0x2a,
    172     0xff,
    173     0x2,
    174     0x27,
    175     0x4,
    176     0x25,
    177     0x0f,
    178     0x40
    179   },
    180   {
    181     0x0f,
    182     0x54,
    183     0xff,
    184     0x2,
    185     0x4f,
    186     0x4,
    187     0x25,
    188     0x0f,
    189     0x0
    190   },
    191   {
    192     0x09,
    193     0x50,
    194     0xff,
    195     0x2,
    196     0x4f,
    197     0x4,
    198     0x25,
    199     0x0f,
    200     0x80
    201   },
    202   {
    203     0x09,
    204     0x2a,
    205     0xff,
    206     0x2,
    207     0x4f,
    208     0x4,
    209     0x25,
    210     0x0f,
    211     0x80
    212   },
    213   {
    214     0x12,
    215     0x1b,
    216     0xff,
    217     0x2,
    218     0x4f,
    219     0x4,
    220     0x25,
    221     0x0f,
    222     0x0
    223   },
    224   {
    225     0x09,
    226     0x2a,
    227     0xff,
    228     0x2,
    229     0x4f,
    230     0x4,
    231     0x25,
    232     0x0f,
    233     0x80
    234   },
    235   {
    236     0x12,
    237     0x1b,
    238     0xff,
    239     0x2,
    240     0x4f,
    241     0x4,
    242     0x25,
    243     0x0f,
    244     0x0
    245   },
    246   {
    247     0x24,
    248     0x1b,
    249     0xff,
    250     0x2,
    251     0x4f,
    252     0x4,
    253     0x25,
    254     0x0f,
    255     0xc0
    256   }
    257 };
    258 
    259 //
    260 // Byte per sector corresponding to various device types.
    261 //
    262 UINTN    BytePerSector[6] = { 0, 256, 512, 1024, 2048, 4096 };
    263 
    264 FDC_BLK_IO_DEV mBlockIoDevTemplate = {
    265   FDC_BLK_IO_DEV_SIGNATURE,
    266   {
    267     FdcGetNumberOfBlockDevices,
    268     FdcGetBlockDeviceMediaInfo,
    269     FdcReadBlocks,
    270   },
    271   {
    272     (EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST),
    273     &gEfiPeiVirtualBlockIoPpiGuid,
    274     NULL
    275   },
    276   0,
    277   {{0}}
    278 };
    279 
    280 /**
    281   Wait and check if bits for DIO and RQM of FDC Main Status Register
    282   indicates FDC is ready for read or write.
    283 
    284   Before writing to FDC or reading from FDC, the Host must examine
    285   the bit7(RQM) and bit6(DIO) of the Main Status Register.
    286   That is to say:
    287    Command bytes can not be written to Data Register unless RQM is 1 and DIO is 0.
    288    Result bytes can not be read from Data Register unless RQM is 1 and DIO is 1.
    289 
    290   @param  FdcBlkIoDev       Instance of FDC_BLK_IO_DEV.
    291   @param  DataIn            Indicates data input or output.
    292                             TRUE means input.
    293                             FALSE means output.
    294   @param  TimeoutInMseconds  Timeout value to wait.
    295 
    296   @retval EFI_SUCCESS       FDC is ready.
    297   @retval EFI_NOT_READY     FDC is not ready within the specified time period.
    298 
    299 **/
    300 EFI_STATUS
    301 FdcDRQReady (
    302   IN FDC_BLK_IO_DEV   *FdcBlkIoDev,
    303   IN BOOLEAN          DataIn,
    304   IN UINTN            TimeoutInMseconds
    305   )
    306 {
    307   UINTN   Delay;
    308   UINT8   StatusRegister;
    309   UINT8   BitInOut;
    310 
    311   //
    312   // Check bit6 of Main Status Register.
    313   //
    314   BitInOut = 0;
    315   if (DataIn) {
    316     BitInOut = BIT6;
    317   }
    318 
    319   Delay = ((TimeoutInMseconds * STALL_1_MSECOND) / FDC_CHECK_INTERVAL) + 1;
    320   do {
    321     StatusRegister = IoRead8 ((UINT16) (PcdGet16 (PcdFdcBaseAddress) + FDC_REGISTER_MSR));
    322     if ((StatusRegister & MSR_RQM) == MSR_RQM && (StatusRegister & MSR_DIO) == BitInOut) {
    323       //
    324       // FDC is ready
    325       //
    326       break;
    327     }
    328 
    329     MicroSecondDelay (FDC_SHORT_DELAY);
    330   } while (--Delay > 0);
    331 
    332   if (Delay == 0) {
    333     //
    334     // FDC is not ready within the specified time period
    335     //
    336     return EFI_NOT_READY;
    337   }
    338 
    339   return EFI_SUCCESS;
    340 }
    341 
    342 /**
    343   Read a byte from FDC data register.
    344 
    345   @param  FdcBlkIoDev      Instance of FDC_BLK_IO_DEV.
    346   @param  Pointer          Pointer to buffer to hold data read from FDC.
    347 
    348   @retval EFI_SUCCESS      Byte successfully read.
    349   @retval EFI_DEVICE_ERROR FDC is not ready.
    350 
    351 **/
    352 EFI_STATUS
    353 DataInByte (
    354   IN  FDC_BLK_IO_DEV   *FdcBlkIoDev,
    355   OUT UINT8            *Pointer
    356   )
    357 {
    358   UINT8 Data;
    359 
    360   //
    361   // Wait for 1ms and detect the FDC is ready to be read
    362   //
    363   if (FdcDRQReady (FdcBlkIoDev, TRUE, 1) != EFI_SUCCESS) {
    364     //
    365     // FDC is not ready.
    366     //
    367     return EFI_DEVICE_ERROR;
    368   }
    369 
    370   Data = IoRead8 ((UINT16) (PcdGet16 (PcdFdcBaseAddress) + FDC_REGISTER_DTR));
    371   MicroSecondDelay (FDC_SHORT_DELAY);
    372   *Pointer = Data;
    373 
    374   return EFI_SUCCESS;
    375 }
    376 
    377 /**
    378   Write a byte to FDC data register.
    379 
    380   @param  FdcBlkIoDev      Instance of FDC_BLK_IO_DEV.
    381   @param  Pointer          Pointer to data to write.
    382 
    383   @retval EFI_SUCCESS      Byte successfully written.
    384   @retval EFI_DEVICE_ERROR FDC is not ready.
    385 
    386 **/
    387 EFI_STATUS
    388 DataOutByte (
    389   IN FDC_BLK_IO_DEV   *FdcBlkIoDev,
    390   IN UINT8            *Pointer
    391   )
    392 {
    393   UINT8 Data;
    394 
    395   //
    396   // Wait for 1ms and detect the FDC is ready to be written
    397   //
    398   if (FdcDRQReady (FdcBlkIoDev, FALSE, 1) != EFI_SUCCESS) {
    399     //
    400     // FDC is not ready.
    401     //
    402     return EFI_DEVICE_ERROR;
    403   }
    404 
    405   Data = *Pointer;
    406   IoWrite8 ((UINT16) (PcdGet16 (PcdFdcBaseAddress) + FDC_REGISTER_DTR), Data);
    407   MicroSecondDelay (FDC_SHORT_DELAY);
    408 
    409   return EFI_SUCCESS;
    410 }
    411 
    412 /**
    413   Get Sts0 and Pcn status from FDC
    414 
    415   @param  FdcBlkIoDev      Instance of FDC_BLK_IO_DEV
    416   @param  Sts0             Value of Sts0
    417   @param  Pcn              Value of Pcn
    418 
    419   @retval EFI_SUCCESS      Successfully retrieved status value of Sts0 and Pcn.
    420   @retval EFI_DEVICE_ERROR Fail to send SENSE_INT_STATUS_CMD.
    421   @retval EFI_DEVICE_ERROR Fail to read Sts0.
    422   @retval EFI_DEVICE_ERROR Fail to read Pcn.
    423 
    424 **/
    425 EFI_STATUS
    426 SenseIntStatus (
    427   IN  FDC_BLK_IO_DEV   *FdcBlkIoDev,
    428   OUT UINT8            *Sts0,
    429   OUT UINT8            *Pcn
    430   )
    431 {
    432   UINT8 Command;
    433 
    434   Command = SENSE_INT_STATUS_CMD;
    435 
    436   if (DataOutByte (FdcBlkIoDev, &Command) != EFI_SUCCESS) {
    437     return EFI_DEVICE_ERROR;
    438   }
    439 
    440   if (DataInByte (FdcBlkIoDev, Sts0) != EFI_SUCCESS) {
    441     return EFI_DEVICE_ERROR;
    442   }
    443 
    444   if (DataInByte (FdcBlkIoDev, Pcn) != EFI_SUCCESS) {
    445     return EFI_DEVICE_ERROR;
    446   }
    447 
    448   return EFI_SUCCESS;
    449 }
    450 
    451 /**
    452   Issue Specify command.
    453 
    454   @param  FdcBlkIoDev      Instance of FDC_BLK_IO_DEV.
    455 
    456   @retval EFI_SUCCESS      Specify command successfully issued.
    457   @retval EFI_DEVICE_ERROR FDC device has errors.
    458 
    459 **/
    460 EFI_STATUS
    461 Specify (
    462   IN FDC_BLK_IO_DEV   *FdcBlkIoDev
    463   )
    464 {
    465   FDC_SPECIFY_CMD Command;
    466   UINTN           Index;
    467   UINT8           *Pointer;
    468 
    469   ZeroMem (&Command, sizeof (FDC_SPECIFY_CMD));
    470   Command.CommandCode = SPECIFY_CMD;
    471   //
    472   // set SRT, HUT
    473   //
    474   Command.SrtHut = 0xdf;
    475   //
    476   // 0xdf;
    477   // set HLT and DMA
    478   //
    479   Command.HltNd = 0x02;
    480 
    481   Pointer            = (UINT8 *) (&Command);
    482   for (Index = 0; Index < sizeof (FDC_SPECIFY_CMD); Index++) {
    483     if (DataOutByte (FdcBlkIoDev, Pointer++) != EFI_SUCCESS) {
    484       return EFI_DEVICE_ERROR;
    485     }
    486   }
    487 
    488   return EFI_SUCCESS;
    489 }
    490 
    491 /**
    492   Wait until busy bit is cleared.
    493 
    494   @param  FdcBlkIoDev      Instance of FDC_BLK_IO_DEV.
    495   @param  DevPos           Position of FDC (Driver A or B)
    496   @param  TimeoutInMseconds Timeout value to wait.
    497 
    498   @retval EFI_SUCCESS      Busy bit has been cleared before timeout.
    499   @retval EFI_TIMEOUT      Time goes out before busy bit is cleared.
    500 
    501 **/
    502 EFI_STATUS
    503 FdcWaitForBSYClear (
    504   IN FDC_BLK_IO_DEV   *FdcBlkIoDev,
    505   IN UINT8            DevPos,
    506   IN UINTN            TimeoutInMseconds
    507   )
    508 {
    509   UINTN   Delay;
    510   UINT8   StatusRegister;
    511   UINT8   Mask;
    512 
    513   //
    514   // How to determine drive and command are busy or not: by the bits of Main Status Register
    515   // bit0: Drive 0 busy (drive A)
    516   // bit1: Drive 1 busy (drive B)
    517   // bit4: Command busy
    518   //
    519   // set mask: for drive A set bit0 & bit4; for drive B set bit1 & bit4
    520   //
    521   Mask  = (UINT8) ((DevPos == 0 ? MSR_DAB : MSR_DBB) | MSR_CB);
    522 
    523   Delay = ((TimeoutInMseconds * STALL_1_MSECOND) / FDC_CHECK_INTERVAL) + 1;
    524 
    525   do {
    526     StatusRegister = IoRead8 ((UINT16) (PcdGet16 (PcdFdcBaseAddress) + FDC_REGISTER_MSR));
    527 
    528     if ((StatusRegister & Mask) == 0x00) {
    529       //
    530       // not busy
    531       //
    532       break;
    533     }
    534 
    535     MicroSecondDelay (FDC_SHORT_DELAY);
    536   } while (--Delay > 0);
    537 
    538   if (Delay == 0) {
    539     return EFI_TIMEOUT;
    540   }
    541 
    542   return EFI_SUCCESS;
    543 }
    544 
    545 /**
    546   Reset FDC device.
    547 
    548   @param  FdcBlkIoDev  Instance of FDC_BLK_IO_DEV
    549   @param  DevPos       Index of FDC device.
    550 
    551   @retval EFI_SUCCESS      FDC device successfully reset.
    552   @retval EFI_DEVICE_ERROR Fail to reset FDC device.
    553 
    554 **/
    555 EFI_STATUS
    556 FdcReset (
    557   IN FDC_BLK_IO_DEV   *FdcBlkIoDev,
    558   IN UINT8            DevPos
    559   )
    560 {
    561   UINT8 Data;
    562   UINT8 Sts0;
    563   UINT8 Pcn;
    564   UINTN Index;
    565 
    566   //
    567   // Reset specified Floppy Logic Drive according to Fdd -> Disk
    568   // Set Digital Output Register(DOR) to do reset work
    569   //    bit0 & bit1 of DOR : Drive Select
    570   //    bit2 : Reset bit
    571   //    bit3 : DMA and Int bit
    572   // Reset : A "0" written to bit2 resets the FDC, this reset will remain active until
    573   //       a "1" is written to this bit.
    574   // Reset step 1:
    575   //    use bit0 & bit1 to  select the logic drive
    576   //    write "0" to bit2
    577   //
    578   Data = 0x0;
    579   Data = (UINT8) (Data | (SELECT_DRV & DevPos));
    580   IoWrite8 ((UINT16) (PcdGet16 (PcdFdcBaseAddress) + FDC_REGISTER_DOR), Data);
    581 
    582   //
    583   // Wait some time, at least 120us.
    584   //
    585   MicroSecondDelay (FDC_RESET_DELAY);
    586   //
    587   // Reset step 2:
    588   //    write "1" to bit2
    589   //    write "1" to bit3 : enable DMA
    590   //
    591   Data |= 0x0C;
    592   IoWrite8 ((UINT16) (PcdGet16 (PcdFdcBaseAddress) + FDC_REGISTER_DOR), Data);
    593 
    594   MicroSecondDelay (FDC_RESET_DELAY);
    595 
    596   //
    597   // Wait until specified floppy logic drive is not busy
    598   //
    599   if (FdcWaitForBSYClear (FdcBlkIoDev, DevPos, 1) != EFI_SUCCESS) {
    600     return EFI_DEVICE_ERROR;
    601   }
    602   //
    603   // Set the Transfer Data Rate
    604   //
    605   IoWrite8 ((UINT16) (PcdGet16 (PcdFdcBaseAddress) + FDC_REGISTER_CCR), 0x0);
    606 
    607   MicroSecondDelay (FDC_MEDIUM_DELAY);
    608 
    609   //
    610   // Issue Sense interrupt command for each drive (totally 4 drives)
    611   //
    612   for (Index = 0; Index < 4; Index++) {
    613     if (SenseIntStatus (FdcBlkIoDev, &Sts0, &Pcn) != EFI_SUCCESS) {
    614       return EFI_DEVICE_ERROR;
    615     }
    616   }
    617   //
    618   // Issue Specify command
    619   //
    620   if (Specify (FdcBlkIoDev) != EFI_SUCCESS) {
    621     return EFI_DEVICE_ERROR;
    622   }
    623 
    624   return EFI_SUCCESS;
    625 }
    626 
    627 /**
    628   Turn on the motor of floppy drive.
    629 
    630   @param  FdcBlkIoDev      Instance of FDC_BLK_IO_DEV.
    631   @param  Info             Information of floppy device.
    632 
    633   @retval EFI_SUCCESS      Motor is successfully turned on.
    634   @retval EFI_SUCCESS      Motor is already on.
    635   @retval EFI_DEVICE_ERROR Busy bit of FDC cannot be cleared.
    636 
    637 **/
    638 EFI_STATUS
    639 MotorOn (
    640   IN FDC_BLK_IO_DEV             *FdcBlkIoDev,
    641   IN OUT PEI_FLOPPY_DEVICE_INFO *Info
    642   )
    643 {
    644   UINT8 Data;
    645   UINT8 DevPos;
    646 
    647   //
    648   // Control of the floppy drive motors is a big pain. If motor is off, you have to turn it
    649   // on first. But you can not leave the motor on all the time, since that would wear out the
    650   // disk. On the other hand, if you turn the motor off after each operation, the system performance
    651   // will be awful. The compromise used in this driver is to leave the motor on for 2 seconds after
    652   // each operation. If a new operation is started in that interval(2s), the motor need not be
    653   // turned on again. If no new operation is started, a timer goes off and the motor is turned off.
    654   //
    655   DevPos = Info->DevPos;
    656 
    657   //
    658   // If the Motor is already on, just return EFI_SUCCESS.
    659   //
    660   if (Info->MotorOn) {
    661     return EFI_SUCCESS;
    662   }
    663   //
    664   // The drive's motor is off, so need turn it on.
    665   // First check if command and drive are busy or not.
    666   //
    667   if (FdcWaitForBSYClear (FdcBlkIoDev, DevPos, 1) != EFI_SUCCESS) {
    668     return EFI_DEVICE_ERROR;
    669   }
    670   //
    671   // for drive A: 1CH, drive B: 2DH
    672   //
    673   Data = 0x0C;
    674   Data = (UINT8) (Data | (SELECT_DRV & DevPos));
    675   if (DevPos == 0) {
    676     Data |= DRVA_MOTOR_ON;
    677   } else {
    678     Data |= DRVB_MOTOR_ON;
    679   }
    680 
    681   Info->MotorOn = FALSE;
    682 
    683   //
    684   // Turn on the motor and wait for some time to ensure it takes effect.
    685   //
    686   IoWrite8 ((UINT16) (PcdGet16 (PcdFdcBaseAddress) + FDC_REGISTER_DOR), Data);
    687   MicroSecondDelay (FDC_LONG_DELAY);
    688 
    689   Info->MotorOn = TRUE;
    690 
    691   return EFI_SUCCESS;
    692 }
    693 
    694 /**
    695   Turn off the motor of floppy drive.
    696 
    697   @param  FdcBlkIoDev      Instance of FDC_BLK_IO_DEV.
    698   @param  Info             Information of floppy device.
    699 
    700 **/
    701 VOID
    702 MotorOff (
    703   IN FDC_BLK_IO_DEV             *FdcBlkIoDev,
    704   IN OUT PEI_FLOPPY_DEVICE_INFO *Info
    705   )
    706 {
    707   UINT8 Data;
    708   UINT8 DevPos;
    709 
    710   DevPos = Info->DevPos;
    711 
    712   if (!Info->MotorOn) {
    713     return;
    714   }
    715   //
    716   // The motor is on, so need motor off
    717   //
    718   Data = 0x0C;
    719   Data = (UINT8) (Data | (SELECT_DRV & DevPos));
    720 
    721   IoWrite8 ((UINT16) (PcdGet16 (PcdFdcBaseAddress) + FDC_REGISTER_DOR), Data);
    722   MicroSecondDelay (FDC_SHORT_DELAY);
    723 
    724   Info->MotorOn = FALSE;
    725 }
    726 
    727 /**
    728   Recalibrate the FDC device.
    729 
    730   @param  FdcBlkIoDev      Instance of FDC_BLK_IO_DEV.
    731   @param  Info             Information of floppy device.
    732 
    733   @retval EFI_SUCCESS      FDC successfully recalibrated.
    734   @retval EFI_DEVICE_ERROR Fail to send RECALIBRATE_CMD.
    735   @retval EFI_DEVICE_ERROR Fail to get status value of Sts0 and Pcn.
    736   @retval EFI_DEVICE_ERROR Fail to recalibrate FDC device.
    737 
    738 **/
    739 EFI_STATUS
    740 Recalibrate (
    741   IN FDC_BLK_IO_DEV             *FdcBlkIoDev,
    742   IN OUT PEI_FLOPPY_DEVICE_INFO *Info
    743   )
    744 {
    745   FDC_COMMAND_PACKET2 Command;
    746   UINTN               Index;
    747   UINT8               Sts0;
    748   UINT8               Pcn;
    749   UINT8               *Pointer;
    750   UINT8               Count;
    751   UINT8               DevPos;
    752 
    753   DevPos  = Info->DevPos;
    754 
    755   //
    756   // We would try twice.
    757   //
    758   Count   = 2;
    759   while (Count > 0) {
    760     ZeroMem (&Command, sizeof (FDC_COMMAND_PACKET2));
    761     Command.CommandCode = RECALIBRATE_CMD;
    762     //
    763     // drive select
    764     //
    765     if (DevPos == 0) {
    766       Command.DiskHeadSel = 0;
    767     } else {
    768       Command.DiskHeadSel = 1;
    769     }
    770 
    771     Pointer = (UINT8 *) (&Command);
    772     for (Index = 0; Index < sizeof (FDC_COMMAND_PACKET2); Index++) {
    773       if (DataOutByte (FdcBlkIoDev, Pointer++) != EFI_SUCCESS) {
    774         return EFI_DEVICE_ERROR;
    775       }
    776     }
    777 
    778     MicroSecondDelay (FDC_RECALIBRATE_DELAY);
    779 
    780     if (SenseIntStatus (FdcBlkIoDev, &Sts0, &Pcn) != EFI_SUCCESS) {
    781       return EFI_DEVICE_ERROR;
    782     }
    783 
    784     if ((Sts0 & 0xf0) == BIT5 && Pcn == 0) {
    785       //
    786       // Recalibration is successful.
    787       //
    788       Info->Pcn = 0;
    789       Info->NeedRecalibrate = FALSE;
    790 
    791       return EFI_SUCCESS;
    792     } else {
    793       //
    794       // Recalibration is not successful. Try again.
    795       // If trial is used out, return EFI_DEVICE_ERROR.
    796       //
    797       Count--;
    798       if (Count == 0) {
    799         return EFI_DEVICE_ERROR;
    800       }
    801     }
    802   }
    803 
    804   return EFI_SUCCESS;
    805 }
    806 
    807 /**
    808   Seek for the cylinder according to given LBA.
    809 
    810   @param  FdcBlkIoDev      Instance of FDC_BLK_IO_DEV.
    811   @param  Info             Information of floppy device.
    812   @param  Lba              LBA for which to seek for cylinder.
    813 
    814   @retval EFI_SUCCESS      Successfully moved to the destination cylinder.
    815   @retval EFI_SUCCESS      Destination cylinder is just the present cylinder.
    816   @retval EFI_DEVICE_ERROR Fail to move to the destination cylinder.
    817 
    818 **/
    819 EFI_STATUS
    820 Seek (
    821   IN     FDC_BLK_IO_DEV         *FdcBlkIoDev,
    822   IN OUT PEI_FLOPPY_DEVICE_INFO *Info,
    823   IN     EFI_PEI_LBA            Lba
    824   )
    825 {
    826   FDC_SEEK_CMD      Command;
    827   DISKET_PARA_TABLE *Para;
    828   UINT8             EndOfTrack;
    829   UINT8             Head;
    830   UINT8             Cylinder;
    831   UINT8             Sts0;
    832   UINT8             *Pointer;
    833   UINT8             Pcn;
    834   UINTN             Index;
    835   UINT8             Gap;
    836   UINT8             DevPos;
    837 
    838   DevPos = Info->DevPos;
    839   if (Info->NeedRecalibrate) {
    840     if (Recalibrate (FdcBlkIoDev, Info) != EFI_SUCCESS) {
    841       return EFI_DEVICE_ERROR;
    842     }
    843     //
    844     // Recalibrate Success
    845     //
    846     Info->NeedRecalibrate = FALSE;
    847   }
    848 
    849   //
    850   // Get the base of disk parameter information corresponding to its type.
    851   //
    852   Para        = (DISKET_PARA_TABLE *) ((UINT8 *) DiskPara + sizeof (DISKET_PARA_TABLE) * Info->Type);
    853   EndOfTrack  = Para->EndOfTrack;
    854   //
    855   // Calculate cylinder based on Lba and EOT
    856   //
    857   Cylinder = (UINT8) ((UINTN) Lba / EndOfTrack / 2);
    858 
    859   //
    860   // If the dest cylinder is the present cylinder, unnecessary to do the seek operation
    861   //
    862   if (Info->Pcn == Cylinder) {
    863     return EFI_SUCCESS;
    864   }
    865 
    866   //
    867   // Calculate the head : 0 or 1
    868   //
    869   Head = (UINT8) ((UINTN) Lba / EndOfTrack % 2);
    870 
    871   ZeroMem (&Command, sizeof (FDC_SEEK_CMD));
    872   Command.CommandCode = SEEK_CMD;
    873   if (DevPos == 0) {
    874     Command.DiskHeadSel = 0;
    875   } else {
    876     Command.DiskHeadSel = 1;
    877   }
    878 
    879   //
    880   // Send command to move to destination cylinder.
    881   //
    882   Command.DiskHeadSel = (UINT8) (Command.DiskHeadSel | (Head << 2));
    883   Command.NewCylinder = Cylinder;
    884 
    885   Pointer = (UINT8 *) (&Command);
    886   for (Index = 0; Index < sizeof (FDC_SEEK_CMD); Index++) {
    887     if (DataOutByte (FdcBlkIoDev, Pointer++) != EFI_SUCCESS) {
    888       return EFI_DEVICE_ERROR;
    889     }
    890   }
    891 
    892   MicroSecondDelay (FDC_SHORT_DELAY);
    893 
    894   //
    895   // Calculate waiting time, which is proportional to the gap between destination
    896   // cylinder and present cylinder.
    897   //
    898   if (Info->Pcn > Cylinder) {
    899     Gap = (UINT8) (Info->Pcn - Cylinder);
    900   } else {
    901     Gap = (UINT8) (Cylinder - Info->Pcn);
    902   }
    903 
    904   MicroSecondDelay ((Gap + 1) * FDC_LONG_DELAY);
    905 
    906   //
    907   // Confirm if the new cylinder is the destination and status is correct.
    908   //
    909   if (SenseIntStatus (FdcBlkIoDev, &Sts0, &Pcn) != EFI_SUCCESS) {
    910     return EFI_DEVICE_ERROR;
    911   }
    912 
    913   if ((Sts0 & 0xf0) == BIT5) {
    914     Info->Pcn             = Command.NewCylinder;
    915     Info->NeedRecalibrate = FALSE;
    916     return EFI_SUCCESS;
    917   } else {
    918     Info->NeedRecalibrate = TRUE;
    919     return EFI_DEVICE_ERROR;
    920   }
    921 }
    922 
    923 /**
    924   Check if diskette is changed.
    925 
    926   @param  FdcBlkIoDev       Instance of FDC_BLK_IO_DEV
    927   @param  Info              Information of floppy device.
    928 
    929   @retval EFI_SUCCESS       Diskette is not changed.
    930   @retval EFI_MEDIA_CHANGED Diskette is changed.
    931   @retval EFI_NO_MEDIA      No diskette.
    932   @retval EFI_DEVICE_ERROR  Fail to do the seek or recalibrate operation.
    933 
    934 **/
    935 EFI_STATUS
    936 DisketChanged (
    937   IN FDC_BLK_IO_DEV             *FdcBlkIoDev,
    938   IN OUT PEI_FLOPPY_DEVICE_INFO *Info
    939   )
    940 {
    941   EFI_STATUS  Status;
    942   UINT8       Data;
    943 
    944   //
    945   // Check change line
    946   //
    947   Data = IoRead8 ((UINT16) (PcdGet16 (PcdFdcBaseAddress) + FDC_REGISTER_DIR));
    948 
    949   MicroSecondDelay (FDC_SHORT_DELAY);
    950 
    951   if ((Data & DIR_DCL) == DIR_DCL) {
    952     if (Info->Pcn != 0) {
    953       Status = Recalibrate (FdcBlkIoDev, Info);
    954     } else {
    955       Status = Seek (FdcBlkIoDev, Info, 0x30);
    956     }
    957 
    958     if (Status != EFI_SUCCESS) {
    959       //
    960       // Fail to do the seek or recalibrate operation
    961       //
    962       return EFI_DEVICE_ERROR;
    963     }
    964 
    965     Data = IoRead8 ((UINT16) (PcdGet16 (PcdFdcBaseAddress) + FDC_REGISTER_DIR));
    966 
    967     MicroSecondDelay (FDC_SHORT_DELAY);
    968 
    969     if ((Data & DIR_DCL) == DIR_DCL) {
    970       return EFI_NO_MEDIA;
    971     }
    972 
    973     return EFI_MEDIA_CHANGED;
    974   }
    975 
    976   return EFI_SUCCESS;
    977 }
    978 
    979 /**
    980   Detects if FDC device exists.
    981 
    982   @param  FdcBlkIoDev  Instance of FDC_BLK_IO_DEV
    983   @param  Info         Information of floppy device.
    984   @param  MediaInfo    Information of floppy media.
    985 
    986   @retval TRUE         FDC device exists and is working properly.
    987   @retval FALSE        FDC device does not exist or cannot work properly.
    988 
    989 **/
    990 BOOLEAN
    991 DiscoverFdcDevice (
    992   IN  FDC_BLK_IO_DEV             *FdcBlkIoDev,
    993   IN  OUT PEI_FLOPPY_DEVICE_INFO *Info,
    994   OUT EFI_PEI_BLOCK_IO_MEDIA     *MediaInfo
    995   )
    996 {
    997   EFI_STATUS        Status;
    998   DISKET_PARA_TABLE *Para;
    999 
   1000   Status = MotorOn (FdcBlkIoDev, Info);
   1001   if (Status != EFI_SUCCESS) {
   1002     return FALSE;
   1003   }
   1004 
   1005   Status = Recalibrate (FdcBlkIoDev, Info);
   1006 
   1007   if (Status != EFI_SUCCESS) {
   1008     MotorOff (FdcBlkIoDev, Info);
   1009     return FALSE;
   1010   }
   1011   //
   1012   // Set Media Parameter
   1013   //
   1014   MediaInfo->DeviceType   = LegacyFloppy;
   1015   MediaInfo->MediaPresent = TRUE;
   1016 
   1017   //
   1018   // Check Media
   1019   //
   1020   Status = DisketChanged (FdcBlkIoDev, Info);
   1021   if (Status == EFI_NO_MEDIA) {
   1022     //
   1023     // No diskette in floppy.
   1024     //
   1025     MediaInfo->MediaPresent = FALSE;
   1026   } else if (Status != EFI_MEDIA_CHANGED && Status != EFI_SUCCESS) {
   1027     //
   1028     // EFI_DEVICE_ERROR
   1029     //
   1030     MotorOff (FdcBlkIoDev, Info);
   1031     return FALSE;
   1032   }
   1033 
   1034   MotorOff (FdcBlkIoDev, Info);
   1035 
   1036   //
   1037   // Get the base of disk parameter information corresponding to its type.
   1038   //
   1039   Para                  = (DISKET_PARA_TABLE *) ((UINT8 *) DiskPara + sizeof (DISKET_PARA_TABLE) * Info->Type);
   1040 
   1041   MediaInfo->BlockSize  = BytePerSector[Para->Number];
   1042   MediaInfo->LastBlock  = Para->EndOfTrack * 2 * (Para->MaxTrackNum + 1) - 1;
   1043 
   1044   return TRUE;
   1045 }
   1046 
   1047 /**
   1048   Enumerate floppy device
   1049 
   1050   @param  FdcBlkIoDev  Instance of floppy device controller
   1051 
   1052   @return Number of FDC devices.
   1053 
   1054 **/
   1055 UINT8
   1056 FdcEnumeration (
   1057   IN FDC_BLK_IO_DEV   *FdcBlkIoDev
   1058   )
   1059 {
   1060   UINT8                   DevPos;
   1061   UINT8                   DevNo;
   1062   EFI_PEI_BLOCK_IO_MEDIA  MediaInfo;
   1063   EFI_STATUS              Status;
   1064 
   1065   DevNo = 0;
   1066 
   1067   //
   1068   // DevPos=0 means Drive A, 1 means Drive B.
   1069   //
   1070   for (DevPos = 0; DevPos < 2; DevPos++) {
   1071     //
   1072     // Detecting device presence
   1073     //
   1074     REPORT_STATUS_CODE (EFI_PROGRESS_CODE, EFI_PERIPHERAL_REMOVABLE_MEDIA + EFI_P_PC_PRESENCE_DETECT);
   1075 
   1076     //
   1077     // Reset FDC
   1078     //
   1079     Status = FdcReset (FdcBlkIoDev, DevPos);
   1080 
   1081     if (EFI_ERROR (Status)) {
   1082       continue;
   1083     }
   1084 
   1085     FdcBlkIoDev->DeviceInfo[DevPos].DevPos          = DevPos;
   1086     FdcBlkIoDev->DeviceInfo[DevPos].Pcn             = 0;
   1087     FdcBlkIoDev->DeviceInfo[DevPos].MotorOn         = FALSE;
   1088     FdcBlkIoDev->DeviceInfo[DevPos].NeedRecalibrate = TRUE;
   1089     FdcBlkIoDev->DeviceInfo[DevPos].Type            = FdcType1440K1440K;
   1090 
   1091     //
   1092     // Discover FDC device
   1093     //
   1094     if (DiscoverFdcDevice (FdcBlkIoDev, &(FdcBlkIoDev->DeviceInfo[DevPos]), &MediaInfo)) {
   1095       FdcBlkIoDev->DeviceInfo[DevNo].DevPos           = DevPos;
   1096 
   1097       FdcBlkIoDev->DeviceInfo[DevNo].Pcn              = FdcBlkIoDev->DeviceInfo[DevPos].Pcn;
   1098       FdcBlkIoDev->DeviceInfo[DevNo].MotorOn          = FdcBlkIoDev->DeviceInfo[DevPos].MotorOn;
   1099       FdcBlkIoDev->DeviceInfo[DevNo].NeedRecalibrate  = FdcBlkIoDev->DeviceInfo[DevPos].NeedRecalibrate;
   1100       FdcBlkIoDev->DeviceInfo[DevNo].Type             = FdcBlkIoDev->DeviceInfo[DevPos].Type;
   1101 
   1102       CopyMem (
   1103         &(FdcBlkIoDev->DeviceInfo[DevNo].MediaInfo),
   1104         &MediaInfo,
   1105         sizeof (EFI_PEI_BLOCK_IO_MEDIA)
   1106         );
   1107 
   1108       DevNo++;
   1109     } else {
   1110       //
   1111       // Assume controller error
   1112       //
   1113       REPORT_STATUS_CODE (
   1114         EFI_ERROR_CODE | EFI_ERROR_MINOR,
   1115         EFI_PERIPHERAL_REMOVABLE_MEDIA + EFI_P_EC_CONTROLLER_ERROR
   1116         );
   1117     }
   1118   }
   1119 
   1120   FdcBlkIoDev->DeviceCount = DevNo;
   1121   return DevNo;
   1122 }
   1123 
   1124 /**
   1125   Checks result reflected by FDC_RESULT_PACKET.
   1126 
   1127   @param  Result           FDC_RESULT_PACKET read from FDC after certain operation.
   1128   @param  Info             Information of floppy device.
   1129 
   1130   @retval EFI_SUCCESS      Result is healthy.
   1131   @retval EFI_DEVICE_ERROR Result is not healthy.
   1132 
   1133 **/
   1134 EFI_STATUS
   1135 CheckResult (
   1136   IN  FDC_RESULT_PACKET         *Result,
   1137   OUT PEI_FLOPPY_DEVICE_INFO    *Info
   1138   )
   1139 {
   1140   if ((Result->Status0 & STS0_IC) != IC_NT) {
   1141     if ((Result->Status0 & STS0_SE) == BIT5) {
   1142       //
   1143       // Seek error
   1144       //
   1145       Info->NeedRecalibrate = TRUE;
   1146     }
   1147 
   1148     Info->NeedRecalibrate = TRUE;
   1149     return EFI_DEVICE_ERROR;
   1150   }
   1151   //
   1152   // Check Status Register1
   1153   //
   1154   if ((Result->Status1 & (STS1_EN | STS1_DE | STS1_OR | STS1_ND | STS1_NW | STS1_MA)) != 0) {
   1155     Info->NeedRecalibrate = TRUE;
   1156     return EFI_DEVICE_ERROR;
   1157   }
   1158   //
   1159   // Check Status Register2
   1160   //
   1161   if ((Result->Status2 & (STS2_CM | STS2_DD | STS2_WC | STS2_BC | STS2_MD)) != 0) {
   1162     Info->NeedRecalibrate = TRUE;
   1163     return EFI_DEVICE_ERROR;
   1164   }
   1165 
   1166   return EFI_SUCCESS;
   1167 }
   1168 
   1169 /**
   1170   Fill parameters for command packet.
   1171 
   1172   @param  Info    Information of floppy device.
   1173   @param  Lba     Logical block address.
   1174   @param  Command Command for which for fill parameters.
   1175 
   1176 **/
   1177 VOID
   1178 FillPara (
   1179   IN  PEI_FLOPPY_DEVICE_INFO *Info,
   1180   IN  EFI_PEI_LBA            Lba,
   1181   OUT FDC_COMMAND_PACKET1    *Command
   1182   )
   1183 {
   1184   DISKET_PARA_TABLE *Para;
   1185   UINT8             EndOfTrack;
   1186   UINT8             DevPos;
   1187 
   1188   DevPos      = Info->DevPos;
   1189 
   1190   //
   1191   // Get the base of disk parameter information corresponding to its type.
   1192   //
   1193   Para        = (DISKET_PARA_TABLE *) ((UINT8 *) DiskPara + sizeof (DISKET_PARA_TABLE) * Info->Type);
   1194 
   1195   EndOfTrack  = Para->EndOfTrack;
   1196 
   1197   if (DevPos == 0) {
   1198     Command->DiskHeadSel = 0;
   1199   } else {
   1200     Command->DiskHeadSel = 1;
   1201   }
   1202 
   1203   Command->Cylinder    = (UINT8) ((UINTN) Lba / EndOfTrack / 2);
   1204   Command->Head        = (UINT8) ((UINTN) Lba / EndOfTrack % 2);
   1205   Command->Sector      = (UINT8) ((UINT8) ((UINTN) Lba % EndOfTrack) + 1);
   1206   Command->DiskHeadSel = (UINT8) (Command->DiskHeadSel | (Command->Head << 2));
   1207   Command->Number      = Para->Number;
   1208   Command->EndOfTrack  = Para->EndOfTrack;
   1209   Command->GapLength   = Para->GapLength;
   1210   Command->DataLength  = Para->DataLength;
   1211 }
   1212 
   1213 /**
   1214   Setup specifed FDC device.
   1215 
   1216   @param  FdcBlkIoDev      Instance of FDC_BLK_IO_DEV.
   1217   @param  DevPos           Index of FDC device.
   1218 
   1219   @retval EFI_SUCCESS      FDC device successfully set up.
   1220   @retval EFI_DEVICE_ERROR FDC device has errors.
   1221 
   1222 **/
   1223 EFI_STATUS
   1224 Setup (
   1225   IN  FDC_BLK_IO_DEV  *FdcBlkIoDev,
   1226   IN  UINT8           DevPos
   1227   )
   1228 {
   1229   EFI_STATUS  Status;
   1230 
   1231   IoWrite8 ((UINT16) (PcdGet16 (PcdFdcBaseAddress) + FDC_REGISTER_CCR), 0x0);
   1232 
   1233   MicroSecondDelay (FDC_MEDIUM_DELAY);
   1234 
   1235   Status = Specify (FdcBlkIoDev);
   1236   return Status;
   1237 }
   1238 
   1239 /**
   1240   Setup DMA channels to read data.
   1241 
   1242   @param  FdcBlkIoDev      Instance of FDC_BLK_IO_DEV.
   1243   @param  Buffer           Memory buffer for DMA transfer.
   1244   @param  BlockSize        the number of the bytes in one block.
   1245   @param  NumberOfBlocks   Number of blocks to read.
   1246 
   1247 **/
   1248 VOID
   1249 SetDMA (
   1250   IN FDC_BLK_IO_DEV   *FdcBlkIoDev,
   1251   IN VOID             *Buffer,
   1252   IN UINTN            BlockSize,
   1253   IN UINTN            NumberOfBlocks
   1254   )
   1255 {
   1256   UINT8 Data;
   1257   UINTN Count;
   1258 
   1259   //
   1260   // Mask DMA channel 2;
   1261   //
   1262   IoWrite8 (R_8237_DMA_WRSMSK_CH0_3, B_8237_DMA_WRSMSK_CMS | 2);
   1263 
   1264   //
   1265   // Clear first/last flip flop
   1266   //
   1267   IoWrite8 (R_8237_DMA_CBPR_CH0_3, B_8237_DMA_WRSMSK_CMS | 2);
   1268 
   1269   //
   1270   // Set mode
   1271   //
   1272   IoWrite8 (R_8237_DMA_CHMODE_CH0_3, V_8237_DMA_CHMODE_SINGLE | V_8237_DMA_CHMODE_IO2MEM | 2);
   1273 
   1274   //
   1275   // Set base address and page register
   1276   //
   1277   Data = (UINT8) (UINTN) Buffer;
   1278   IoWrite8 (R_8237_DMA_BASE_CA_CH2, Data);
   1279   Data = (UINT8) ((UINTN) Buffer >> 8);
   1280   IoWrite8 (R_8237_DMA_BASE_CA_CH2, Data);
   1281 
   1282   Data = (UINT8) ((UINTN) Buffer >> 16);
   1283   IoWrite8 (R_8237_DMA_MEM_LP_CH2, Data);
   1284 
   1285   //
   1286   // Set count register
   1287   //
   1288   Count = BlockSize * NumberOfBlocks - 1;
   1289   Data  = (UINT8) (Count & 0xff);
   1290   IoWrite8 (R_8237_DMA_BASE_CC_CH2, Data);
   1291   Data = (UINT8) (Count >> 8);
   1292   IoWrite8 (R_8237_DMA_BASE_CC_CH2, Data);
   1293 
   1294   //
   1295   // Clear channel 2 mask
   1296   //
   1297   IoWrite8 (R_8237_DMA_WRSMSK_CH0_3, 0x02);
   1298 }
   1299 
   1300 
   1301 /**
   1302   According to the block range specified by Lba and NumberOfBlocks, calculate
   1303   the number of blocks in the same sector, which can be transferred in a batch.
   1304 
   1305   @param  Info           Information of floppy device.
   1306   @param  Lba            Start address of block range.
   1307   @param  NumberOfBlocks Number of blocks of the range.
   1308 
   1309   @return Number of blocks in the same sector.
   1310 
   1311 **/
   1312 UINTN
   1313 GetTransferBlockCount (
   1314   IN  PEI_FLOPPY_DEVICE_INFO *Info,
   1315   IN  EFI_PEI_LBA            Lba,
   1316   IN  UINTN                  NumberOfBlocks
   1317   )
   1318 {
   1319   DISKET_PARA_TABLE *Para;
   1320   UINT8             EndOfTrack;
   1321   UINT8             Head;
   1322   UINT8             SectorsInTrack;
   1323 
   1324   //
   1325   // Get the base of disk parameter information corresponding to its type.
   1326   //
   1327   Para            = (DISKET_PARA_TABLE *) ((UINT8 *) DiskPara + sizeof (DISKET_PARA_TABLE) * Info->Type);
   1328 
   1329   EndOfTrack      = Para->EndOfTrack;
   1330   Head            = (UINT8) ((UINTN) Lba / EndOfTrack % 2);
   1331 
   1332   SectorsInTrack  = (UINT8) (EndOfTrack * (2 - Head) - (UINT8) ((UINTN) Lba % EndOfTrack));
   1333   if (SectorsInTrack < NumberOfBlocks) {
   1334     //
   1335     // Not all the block range locates in the same sector
   1336     //
   1337     return SectorsInTrack;
   1338   } else {
   1339     //
   1340     // All the block range is in the same sector.
   1341     //
   1342     return NumberOfBlocks;
   1343   }
   1344 }
   1345 
   1346 /**
   1347   Read data sector from FDC device.
   1348 
   1349   @param  FdcBlkIoDev      Instance of FDC_BLK_IO_DEV.
   1350   @param  Info             Information of floppy device.
   1351   @param  Buffer           Buffer to setup for DMA.
   1352   @param  Lba              The start address to read.
   1353   @param  NumberOfBlocks   Number of blocks to read.
   1354 
   1355   @retval EFI_SUCCESS      Data successfully read out.
   1356   @retval EFI_DEVICE_ERROR FDC device has errors.
   1357   @retval EFI_TIMEOUT      Command does not take effect in time.
   1358 
   1359 **/
   1360 EFI_STATUS
   1361 ReadDataSector (
   1362   IN     FDC_BLK_IO_DEV         *FdcBlkIoDev,
   1363   IN OUT PEI_FLOPPY_DEVICE_INFO *Info,
   1364   IN     VOID                   *Buffer,
   1365   IN     EFI_PEI_LBA            Lba,
   1366   IN     UINTN                  NumberOfBlocks
   1367   )
   1368 {
   1369   EFI_STATUS          Status;
   1370   FDC_COMMAND_PACKET1 Command;
   1371   FDC_RESULT_PACKET   Result;
   1372   UINTN               Index;
   1373   UINTN               Times;
   1374   UINT8               *Pointer;
   1375 
   1376   Status = Seek (FdcBlkIoDev, Info, Lba);
   1377   if (Status != EFI_SUCCESS) {
   1378     return EFI_DEVICE_ERROR;
   1379   }
   1380 
   1381   //
   1382   // Set up DMA
   1383   //
   1384   SetDMA (FdcBlkIoDev, Buffer, Info->MediaInfo.BlockSize, NumberOfBlocks);
   1385 
   1386   //
   1387   // Allocate Read command packet
   1388   //
   1389   ZeroMem (&Command, sizeof (FDC_COMMAND_PACKET1));
   1390   Command.CommandCode = READ_DATA_CMD | CMD_MT | CMD_MFM | CMD_SK;
   1391 
   1392   //
   1393   // Fill parameters for command.
   1394   //
   1395   FillPara (Info, Lba, &Command);
   1396 
   1397   //
   1398   // Write command bytes to FDC
   1399   //
   1400   Pointer = (UINT8 *) (&Command);
   1401   for (Index = 0; Index < sizeof (FDC_COMMAND_PACKET1); Index++) {
   1402     if (DataOutByte (FdcBlkIoDev, Pointer++) != EFI_SUCCESS) {
   1403       return EFI_DEVICE_ERROR;
   1404     }
   1405   }
   1406 
   1407   //
   1408   // Wait for some time until command takes effect.
   1409   //
   1410   Times = (STALL_1_SECOND / FDC_CHECK_INTERVAL) + 1;
   1411   do {
   1412     if ((IoRead8 ((UINT16) (PcdGet16 (PcdFdcBaseAddress) + FDC_REGISTER_MSR)) & 0xc0) == 0xc0) {
   1413       break;
   1414     }
   1415 
   1416     MicroSecondDelay (FDC_SHORT_DELAY);
   1417   } while (--Times > 0);
   1418 
   1419   if (Times == 0) {
   1420     //
   1421     // Command fails to take effect in time, return EFI_TIMEOUT.
   1422     //
   1423     return EFI_TIMEOUT;
   1424   }
   1425 
   1426   //
   1427   // Read result bytes from FDC
   1428   //
   1429   Pointer = (UINT8 *) (&Result);
   1430   for (Index = 0; Index < sizeof (FDC_RESULT_PACKET); Index++) {
   1431     if (DataInByte (FdcBlkIoDev, Pointer++) != EFI_SUCCESS) {
   1432       return EFI_DEVICE_ERROR;
   1433     }
   1434   }
   1435 
   1436   return CheckResult (&Result, Info);
   1437 }
   1438 
   1439 /**
   1440   Gets the count of block I/O devices that one specific block driver detects.
   1441 
   1442   This function is used for getting the count of block I/O devices that one
   1443   specific block driver detects.  To the PEI ATAPI driver, it returns the number
   1444   of all the detected ATAPI devices it detects during the enumeration process.
   1445   To the PEI legacy floppy driver, it returns the number of all the legacy
   1446   devices it finds during its enumeration process. If no device is detected,
   1447   then the function will return zero.
   1448 
   1449   @param[in]  PeiServices          General-purpose services that are available
   1450                                    to every PEIM.
   1451   @param[in]  This                 Indicates the EFI_PEI_RECOVERY_BLOCK_IO_PPI
   1452                                    instance.
   1453   @param[out] NumberBlockDevices   The number of block I/O devices discovered.
   1454 
   1455   @retval     EFI_SUCCESS          Operation performed successfully.
   1456 
   1457 **/
   1458 EFI_STATUS
   1459 EFIAPI
   1460 FdcGetNumberOfBlockDevices (
   1461   IN   EFI_PEI_SERVICES                  **PeiServices,
   1462   IN   EFI_PEI_RECOVERY_BLOCK_IO_PPI     *This,
   1463   OUT  UINTN                             *NumberBlockDevices
   1464   )
   1465 {
   1466   FDC_BLK_IO_DEV  *FdcBlkIoDev;
   1467 
   1468   FdcBlkIoDev = NULL;
   1469 
   1470   FdcBlkIoDev         = PEI_RECOVERY_FDC_FROM_BLKIO_THIS (This);
   1471 
   1472   *NumberBlockDevices = FdcBlkIoDev->DeviceCount;
   1473 
   1474   return EFI_SUCCESS;
   1475 }
   1476 
   1477 /**
   1478   Gets a block device's media information.
   1479 
   1480   This function will provide the caller with the specified block device's media
   1481   information. If the media changes, calling this function will update the media
   1482   information accordingly.
   1483 
   1484   @param[in]  PeiServices   General-purpose services that are available to every
   1485                             PEIM
   1486   @param[in]  This          Indicates the EFI_PEI_RECOVERY_BLOCK_IO_PPI instance.
   1487   @param[in]  DeviceIndex   Specifies the block device to which the function wants
   1488                             to talk. Because the driver that implements Block I/O
   1489                             PPIs will manage multiple block devices, the PPIs that
   1490                             want to talk to a single device must specify the
   1491                             device index that was assigned during the enumeration
   1492                             process. This index is a number from one to
   1493                             NumberBlockDevices.
   1494   @param[out] MediaInfo     The media information of the specified block media.
   1495                             The caller is responsible for the ownership of this
   1496                             data structure.
   1497 
   1498   @retval EFI_SUCCESS        Media information about the specified block device
   1499                              was obtained successfully.
   1500   @retval EFI_DEVICE_ERROR   Cannot get the media information due to a hardware
   1501                              error.
   1502   @retval Others             Other failure occurs.
   1503 
   1504 **/
   1505 EFI_STATUS
   1506 EFIAPI
   1507 FdcGetBlockDeviceMediaInfo (
   1508   IN   EFI_PEI_SERVICES                     **PeiServices,
   1509   IN   EFI_PEI_RECOVERY_BLOCK_IO_PPI        *This,
   1510   IN   UINTN                                DeviceIndex,
   1511   OUT  EFI_PEI_BLOCK_IO_MEDIA               *MediaInfo
   1512   )
   1513 {
   1514   UINTN           DeviceCount;
   1515   FDC_BLK_IO_DEV  *FdcBlkIoDev;
   1516   BOOLEAN         Healthy;
   1517   UINTN           Index;
   1518 
   1519   FdcBlkIoDev = NULL;
   1520 
   1521   if (This == NULL || MediaInfo == NULL) {
   1522     return EFI_INVALID_PARAMETER;
   1523   }
   1524 
   1525   FdcBlkIoDev = PEI_RECOVERY_FDC_FROM_BLKIO_THIS (This);
   1526 
   1527   DeviceCount = FdcBlkIoDev->DeviceCount;
   1528 
   1529   //
   1530   // DeviceIndex is a value from 1 to NumberBlockDevices.
   1531   //
   1532   if ((DeviceIndex < 1) || (DeviceIndex > DeviceCount) || (DeviceIndex > 2)) {
   1533     return EFI_INVALID_PARAMETER;
   1534   }
   1535 
   1536   Index = DeviceIndex - 1;
   1537   //
   1538   // Probe media and retrieve latest media information
   1539   //
   1540   Healthy = DiscoverFdcDevice (
   1541               FdcBlkIoDev,
   1542               &FdcBlkIoDev->DeviceInfo[Index],
   1543               MediaInfo
   1544               );
   1545 
   1546   if (!Healthy) {
   1547     return EFI_DEVICE_ERROR;
   1548   }
   1549 
   1550   CopyMem (
   1551     &(FdcBlkIoDev->DeviceInfo[Index].MediaInfo),
   1552     MediaInfo,
   1553     sizeof (EFI_PEI_BLOCK_IO_MEDIA)
   1554     );
   1555 
   1556   return EFI_SUCCESS;
   1557 }
   1558 
   1559 /**
   1560   Reads the requested number of blocks from the specified block device.
   1561 
   1562   The function reads the requested number of blocks from the device. All the
   1563   blocks are read, or an error is returned. If there is no media in the device,
   1564   the function returns EFI_NO_MEDIA.
   1565 
   1566   @param[in]  PeiServices   General-purpose services that are available to
   1567                             every PEIM.
   1568   @param[in]  This          Indicates the EFI_PEI_RECOVERY_BLOCK_IO_PPI instance.
   1569   @param[in]  DeviceIndex   Specifies the block device to which the function wants
   1570                             to talk. Because the driver that implements Block I/O
   1571                             PPIs will manage multiple block devices, the PPIs that
   1572                             want to talk to a single device must specify the device
   1573                             index that was assigned during the enumeration process.
   1574                             This index is a number from one to NumberBlockDevices.
   1575   @param[in]  StartLBA      The starting logical block address (LBA) to read from
   1576                             on the device
   1577   @param[in]  BufferSize    The size of the Buffer in bytes. This number must be
   1578                             a multiple of the intrinsic block size of the device.
   1579   @param[out] Buffer        A pointer to the destination buffer for the data.
   1580                             The caller is responsible for the ownership of the
   1581                             buffer.
   1582 
   1583   @retval EFI_SUCCESS             The data was read correctly from the device.
   1584   @retval EFI_DEVICE_ERROR        The device reported an error while attempting
   1585                                   to perform the read operation.
   1586   @retval EFI_INVALID_PARAMETER   The read request contains LBAs that are not
   1587                                   valid, or the buffer is not properly aligned.
   1588   @retval EFI_NO_MEDIA            There is no media in the device.
   1589   @retval EFI_BAD_BUFFER_SIZE     The BufferSize parameter is not a multiple of
   1590                                   the intrinsic block size of the device.
   1591 
   1592 **/
   1593 EFI_STATUS
   1594 EFIAPI
   1595 FdcReadBlocks (
   1596   IN   EFI_PEI_SERVICES                  **PeiServices,
   1597   IN   EFI_PEI_RECOVERY_BLOCK_IO_PPI     *This,
   1598   IN   UINTN                             DeviceIndex,
   1599   IN   EFI_PEI_LBA                       StartLBA,
   1600   IN   UINTN                             BufferSize,
   1601   OUT  VOID                              *Buffer
   1602   )
   1603 {
   1604   EFI_PEI_BLOCK_IO_MEDIA MediaInfo;
   1605   EFI_STATUS            Status;
   1606   UINTN                 Count;
   1607   UINTN                 NumberOfBlocks;
   1608   UINTN                 BlockSize;
   1609   FDC_BLK_IO_DEV        *FdcBlkIoDev;
   1610   VOID                  *MemPage;
   1611 
   1612   FdcBlkIoDev = NULL;
   1613   ZeroMem (&MediaInfo, sizeof (EFI_PEI_BLOCK_IO_MEDIA));
   1614 
   1615   if (This == NULL) {
   1616     return EFI_INVALID_PARAMETER;
   1617   }
   1618 
   1619   FdcBlkIoDev = PEI_RECOVERY_FDC_FROM_BLKIO_THIS (This);
   1620 
   1621   if (Buffer == NULL) {
   1622     return EFI_INVALID_PARAMETER;
   1623   }
   1624 
   1625   Status = FdcGetBlockDeviceMediaInfo (PeiServices, This, DeviceIndex, &MediaInfo);
   1626   if (Status != EFI_SUCCESS) {
   1627     return EFI_DEVICE_ERROR;
   1628   }
   1629 
   1630   if (!MediaInfo.MediaPresent) {
   1631     return EFI_NO_MEDIA;
   1632   }
   1633 
   1634   BlockSize = MediaInfo.BlockSize;
   1635 
   1636   //
   1637   // If BufferSize cannot be divided by block size of FDC device,
   1638   // return EFI_BAD_BUFFER_SIZE.
   1639   //
   1640   if (BufferSize % BlockSize != 0) {
   1641     return EFI_BAD_BUFFER_SIZE;
   1642   }
   1643 
   1644   NumberOfBlocks = BufferSize / BlockSize;
   1645 
   1646   if ((StartLBA + NumberOfBlocks - 1) > FdcBlkIoDev->DeviceInfo[DeviceIndex - 1].MediaInfo.LastBlock) {
   1647     return EFI_INVALID_PARAMETER;
   1648   }
   1649 
   1650   MemPage = AllocatePages (EFI_SIZE_TO_PAGES (BufferSize));
   1651   if ((MemPage == NULL) || ((UINTN) MemPage >= ISA_MAX_MEMORY_ADDRESS)) {
   1652     //
   1653     // If fail to allocate memory under ISA_MAX_MEMORY_ADDRESS, designate the address space for DMA
   1654     //
   1655     MemPage = (VOID *) ((UINTN) (UINT32) 0x0f00000);
   1656   }
   1657   Status = MotorOn (FdcBlkIoDev, &(FdcBlkIoDev->DeviceInfo[DeviceIndex - 1]));
   1658   if (Status != EFI_SUCCESS) {
   1659     return EFI_DEVICE_ERROR;
   1660   }
   1661 
   1662   Status = Setup (FdcBlkIoDev, FdcBlkIoDev->DeviceInfo[DeviceIndex - 1].DevPos);
   1663   if (Status != EFI_SUCCESS) {
   1664     MotorOff (FdcBlkIoDev, &(FdcBlkIoDev->DeviceInfo[DeviceIndex - 1]));
   1665     return EFI_DEVICE_ERROR;
   1666   }
   1667   //
   1668   // Read data in batches.
   1669   // Blocks in the same cylinder are read out in a batch.
   1670   //
   1671   while ((Count = GetTransferBlockCount (
   1672                     &(FdcBlkIoDev->DeviceInfo[DeviceIndex - 1]),
   1673                     StartLBA,
   1674                     NumberOfBlocks
   1675                     )) != 0 && Status == EFI_SUCCESS) {
   1676     Status = ReadDataSector (
   1677                FdcBlkIoDev,
   1678                &(FdcBlkIoDev->DeviceInfo[DeviceIndex - 1]),
   1679                MemPage,
   1680                StartLBA,
   1681                Count
   1682                );
   1683     CopyMem (Buffer, MemPage, BlockSize * Count);
   1684     StartLBA += Count;
   1685     NumberOfBlocks -= Count;
   1686     Buffer = (VOID *) ((UINTN) Buffer + Count * BlockSize);
   1687   }
   1688 
   1689   MotorOff (FdcBlkIoDev, &(FdcBlkIoDev->DeviceInfo[DeviceIndex - 1]));
   1690 
   1691   switch (Status) {
   1692   case EFI_SUCCESS:
   1693     return EFI_SUCCESS;
   1694 
   1695   default:
   1696     FdcReset (FdcBlkIoDev, FdcBlkIoDev->DeviceInfo[DeviceIndex - 1].DevPos);
   1697     return EFI_DEVICE_ERROR;
   1698   }
   1699 }
   1700 
   1701 /**
   1702   Initializes the floppy disk controller and installs FDC Block I/O PPI.
   1703 
   1704   @param  FileHandle            Handle of the file being invoked.
   1705   @param  PeiServices           Describes the list of possible PEI Services.
   1706 
   1707   @retval EFI_SUCCESS           Successfully initialized FDC and installed PPI.
   1708   @retval EFI_NOT_FOUND         Cannot find FDC device.
   1709   @retval EFI_OUT_OF_RESOURCES  Have no enough memory to create instance or descriptors.
   1710   @retval Other                 Fail to install FDC Block I/O PPI.
   1711 
   1712 **/
   1713 EFI_STATUS
   1714 EFIAPI
   1715 FdcPeimEntry (
   1716   IN  EFI_PEI_FILE_HANDLE         FileHandle,
   1717   IN  CONST EFI_PEI_SERVICES      **PeiServices
   1718   )
   1719 {
   1720   EFI_STATUS            Status;
   1721   FDC_BLK_IO_DEV        *FdcBlkIoDev;
   1722   UINTN                 DeviceCount;
   1723   UINT32                Index;
   1724 
   1725   Status = PeiServicesRegisterForShadow (FileHandle);
   1726   if (!EFI_ERROR (Status)) {
   1727     return Status;
   1728   }
   1729 
   1730   //
   1731   // Allocate memory for instance of FDC_BLK_IO_DEV and copy initial value
   1732   // from template to it.
   1733   //
   1734   FdcBlkIoDev = AllocatePages (EFI_SIZE_TO_PAGES(sizeof (FDC_BLK_IO_DEV)));
   1735   if (FdcBlkIoDev == NULL) {
   1736     return EFI_OUT_OF_RESOURCES;
   1737   }
   1738   CopyMem (FdcBlkIoDev, &mBlockIoDevTemplate, sizeof (mBlockIoDevTemplate));
   1739 
   1740   //
   1741   // Initialize DMA controller to enable all channels.
   1742   //
   1743   for (Index = 0; Index < sizeof (mRegisterTable) / sizeof (PEI_DMA_TABLE); Index++) {
   1744     IoWrite8 (mRegisterTable[Index].Register, mRegisterTable[Index].Value);
   1745   }
   1746   REPORT_STATUS_CODE (EFI_PROGRESS_CODE, EFI_PERIPHERAL_REMOVABLE_MEDIA + EFI_P_PC_INIT);
   1747 
   1748   //
   1749   // Enumerate FDC devices.
   1750   //
   1751   DeviceCount = FdcEnumeration (FdcBlkIoDev);
   1752   if (DeviceCount == 0) {
   1753     return EFI_NOT_FOUND;
   1754   }
   1755 
   1756   FdcBlkIoDev->PpiDescriptor.Ppi = &FdcBlkIoDev->FdcBlkIo;
   1757 
   1758   return PeiServicesInstallPpi (&FdcBlkIoDev->PpiDescriptor);
   1759 }
   1760