2 Floppy Peim to support Recovery function from Floppy device.
4 Copyright (c) 2006 - 2014, Intel Corporation. All rights reserved.<BR>
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
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.
18 #include "FloppyPeim.h"
21 PEI_DMA_TABLE mRegisterTable
[] = {
23 // DMA2: Clear Byte Ptr, Enable
26 R_8237_DMA_CBPR_CH4_7
,
30 R_8237_DMA_COMMAND_CH4_7
,
34 // DMA1: Clear Byte Ptr, Enable
37 R_8237_DMA_CBPR_CH0_3
,
41 R_8237_DMA_COMMAND_CH0_3
,
45 // Configure Channel 4 for Cascade Mode
46 // Clear DMA Request and enable DREQ
49 R_8237_DMA_CHMODE_CH4_7
,
50 V_8237_DMA_CHMODE_CASCADE
| 0
57 R_8237_DMA_WRSMSK_CH4_7
,
61 // Configure DMA1 (Channels 0-3) for Single Mode
62 // Clear DMA Request and enable DREQ
65 R_8237_DMA_CHMODE_CH0_3
,
66 V_8237_DMA_CHMODE_SINGLE
| 0
73 R_8237_DMA_WRSMSK_CH0_3
,
77 R_8237_DMA_CHMODE_CH0_3
,
78 V_8237_DMA_CHMODE_SINGLE
| 1
85 R_8237_DMA_WRSMSK_CH0_3
,
89 R_8237_DMA_CHMODE_CH0_3
,
90 V_8237_DMA_CHMODE_SINGLE
| 2
97 R_8237_DMA_WRSMSK_CH0_3
,
101 R_8237_DMA_CHMODE_CH0_3
,
102 V_8237_DMA_CHMODE_SINGLE
| 3
105 R_8237_DMA_STA_CH0_3
,
109 R_8237_DMA_WRSMSK_CH0_3
,
113 // Configure DMA2 (Channels 5-7) for Single Mode
114 // Clear DMA Request and enable DREQ
117 R_8237_DMA_CHMODE_CH4_7
,
118 V_8237_DMA_CHMODE_SINGLE
| 1
121 R_8237_DMA_STA_CH4_7
,
125 R_8237_DMA_WRSMSK_CH4_7
,
129 R_8237_DMA_CHMODE_CH4_7
,
130 V_8237_DMA_CHMODE_SINGLE
| 2
133 R_8237_DMA_STA_CH4_7
,
137 R_8237_DMA_WRSMSK_CH4_7
,
141 R_8237_DMA_CHMODE_CH4_7
,
142 V_8237_DMA_CHMODE_SINGLE
| 3
145 R_8237_DMA_STA_CH4_7
,
149 R_8237_DMA_WRSMSK_CH4_7
,
155 // Table of diskette parameters of various diskette types
157 DISKET_PARA_TABLE DiskPara
[9] = {
260 // Byte per sector corresponding to various device types.
262 UINTN BytePerSector
[6] = { 0, 256, 512, 1024, 2048, 4096 };
264 FDC_BLK_IO_DEV mBlockIoDevTemplate
= {
265 FDC_BLK_IO_DEV_SIGNATURE
,
267 FdcGetNumberOfBlockDevices
,
268 FdcGetBlockDeviceMediaInfo
,
272 (EFI_PEI_PPI_DESCRIPTOR_PPI
| EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST
),
273 &gEfiPeiVirtualBlockIoPpiGuid
,
281 Wait and check if bits for DIO and RQM of FDC Main Status Register
282 indicates FDC is ready for read or write.
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.
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.
290 @param FdcBlkIoDev Instance of FDC_BLK_IO_DEV.
291 @param DataIn Indicates data input or output.
294 @param TimeoutInMseconds Timeout value to wait.
296 @retval EFI_SUCCESS FDC is ready.
297 @retval EFI_NOT_READY FDC is not ready within the specified time period.
302 IN FDC_BLK_IO_DEV
*FdcBlkIoDev
,
304 IN UINTN TimeoutInMseconds
308 UINT8 StatusRegister
;
312 // Check bit6 of Main Status Register.
319 Delay
= ((TimeoutInMseconds
* STALL_1_MSECOND
) / FDC_CHECK_INTERVAL
) + 1;
321 StatusRegister
= IoRead8 ((UINT16
) (PcdGet16 (PcdFdcBaseAddress
) + FDC_REGISTER_MSR
));
322 if ((StatusRegister
& MSR_RQM
) == MSR_RQM
&& (StatusRegister
& MSR_DIO
) == BitInOut
) {
329 MicroSecondDelay (FDC_SHORT_DELAY
);
330 } while (--Delay
> 0);
334 // FDC is not ready within the specified time period
336 return EFI_NOT_READY
;
343 Read a byte from FDC data register.
345 @param FdcBlkIoDev Instance of FDC_BLK_IO_DEV.
346 @param Pointer Pointer to buffer to hold data read from FDC.
348 @retval EFI_SUCCESS Byte successfully read.
349 @retval EFI_DEVICE_ERROR FDC is not ready.
354 IN FDC_BLK_IO_DEV
*FdcBlkIoDev
,
361 // Wait for 1ms and detect the FDC is ready to be read
363 if (FdcDRQReady (FdcBlkIoDev
, TRUE
, 1) != EFI_SUCCESS
) {
367 return EFI_DEVICE_ERROR
;
370 Data
= IoRead8 ((UINT16
) (PcdGet16 (PcdFdcBaseAddress
) + FDC_REGISTER_DTR
));
371 MicroSecondDelay (FDC_SHORT_DELAY
);
378 Write a byte to FDC data register.
380 @param FdcBlkIoDev Instance of FDC_BLK_IO_DEV.
381 @param Pointer Pointer to data to write.
383 @retval EFI_SUCCESS Byte successfully written.
384 @retval EFI_DEVICE_ERROR FDC is not ready.
389 IN FDC_BLK_IO_DEV
*FdcBlkIoDev
,
396 // Wait for 1ms and detect the FDC is ready to be written
398 if (FdcDRQReady (FdcBlkIoDev
, FALSE
, 1) != EFI_SUCCESS
) {
402 return EFI_DEVICE_ERROR
;
406 IoWrite8 ((UINT16
) (PcdGet16 (PcdFdcBaseAddress
) + FDC_REGISTER_DTR
), Data
);
407 MicroSecondDelay (FDC_SHORT_DELAY
);
413 Get Sts0 and Pcn status from FDC
415 @param FdcBlkIoDev Instance of FDC_BLK_IO_DEV
416 @param Sts0 Value of Sts0
417 @param Pcn Value of Pcn
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.
427 IN FDC_BLK_IO_DEV
*FdcBlkIoDev
,
434 Command
= SENSE_INT_STATUS_CMD
;
436 if (DataOutByte (FdcBlkIoDev
, &Command
) != EFI_SUCCESS
) {
437 return EFI_DEVICE_ERROR
;
440 if (DataInByte (FdcBlkIoDev
, Sts0
) != EFI_SUCCESS
) {
441 return EFI_DEVICE_ERROR
;
444 if (DataInByte (FdcBlkIoDev
, Pcn
) != EFI_SUCCESS
) {
445 return EFI_DEVICE_ERROR
;
452 Issue Specify command.
454 @param FdcBlkIoDev Instance of FDC_BLK_IO_DEV.
456 @retval EFI_SUCCESS Specify command successfully issued.
457 @retval EFI_DEVICE_ERROR FDC device has errors.
462 IN FDC_BLK_IO_DEV
*FdcBlkIoDev
465 FDC_SPECIFY_CMD Command
;
469 ZeroMem (&Command
, sizeof (FDC_SPECIFY_CMD
));
470 Command
.CommandCode
= SPECIFY_CMD
;
474 Command
.SrtHut
= 0xdf;
479 Command
.HltNd
= 0x02;
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
;
492 Wait until busy bit is cleared.
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.
498 @retval EFI_SUCCESS Busy bit has been cleared before timeout.
499 @retval EFI_TIMEOUT Time goes out before busy bit is cleared.
504 IN FDC_BLK_IO_DEV
*FdcBlkIoDev
,
506 IN UINTN TimeoutInMseconds
510 UINT8 StatusRegister
;
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
519 // set mask: for drive A set bit0 & bit4; for drive B set bit1 & bit4
521 Mask
= (UINT8
) ((DevPos
== 0 ? MSR_DAB
: MSR_DBB
) | MSR_CB
);
523 Delay
= ((TimeoutInMseconds
* STALL_1_MSECOND
) / FDC_CHECK_INTERVAL
) + 1;
526 StatusRegister
= IoRead8 ((UINT16
) (PcdGet16 (PcdFdcBaseAddress
) + FDC_REGISTER_MSR
));
528 if ((StatusRegister
& Mask
) == 0x00) {
535 MicroSecondDelay (FDC_SHORT_DELAY
);
536 } while (--Delay
> 0);
548 @param FdcBlkIoDev Instance of FDC_BLK_IO_DEV
549 @param DevPos Index of FDC device.
551 @retval EFI_SUCCESS FDC device successfully reset.
552 @retval EFI_DEVICE_ERROR Fail to reset FDC device.
557 IN FDC_BLK_IO_DEV
*FdcBlkIoDev
,
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
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.
575 // use bit0 & bit1 to select the logic drive
579 Data
= (UINT8
) (Data
| (SELECT_DRV
& DevPos
));
580 IoWrite8 ((UINT16
) (PcdGet16 (PcdFdcBaseAddress
) + FDC_REGISTER_DOR
), Data
);
583 // Wait some time, at least 120us.
585 MicroSecondDelay (FDC_RESET_DELAY
);
589 // write "1" to bit3 : enable DMA
592 IoWrite8 ((UINT16
) (PcdGet16 (PcdFdcBaseAddress
) + FDC_REGISTER_DOR
), Data
);
594 MicroSecondDelay (FDC_RESET_DELAY
);
597 // Wait until specified floppy logic drive is not busy
599 if (FdcWaitForBSYClear (FdcBlkIoDev
, DevPos
, 1) != EFI_SUCCESS
) {
600 return EFI_DEVICE_ERROR
;
603 // Set the Transfer Data Rate
605 IoWrite8 ((UINT16
) (PcdGet16 (PcdFdcBaseAddress
) + FDC_REGISTER_CCR
), 0x0);
607 MicroSecondDelay (FDC_MEDIUM_DELAY
);
610 // Issue Sense interrupt command for each drive (totally 4 drives)
612 for (Index
= 0; Index
< 4; Index
++) {
613 if (SenseIntStatus (FdcBlkIoDev
, &Sts0
, &Pcn
) != EFI_SUCCESS
) {
614 return EFI_DEVICE_ERROR
;
618 // Issue Specify command
620 if (Specify (FdcBlkIoDev
) != EFI_SUCCESS
) {
621 return EFI_DEVICE_ERROR
;
628 Turn on the motor of floppy drive.
630 @param FdcBlkIoDev Instance of FDC_BLK_IO_DEV.
631 @param Info Information of floppy device.
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.
640 IN FDC_BLK_IO_DEV
*FdcBlkIoDev
,
641 IN OUT PEI_FLOPPY_DEVICE_INFO
*Info
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.
655 DevPos
= Info
->DevPos
;
658 // If the Motor is already on, just return EFI_SUCCESS.
664 // The drive's motor is off, so need turn it on.
665 // First check if command and drive are busy or not.
667 if (FdcWaitForBSYClear (FdcBlkIoDev
, DevPos
, 1) != EFI_SUCCESS
) {
668 return EFI_DEVICE_ERROR
;
671 // for drive A: 1CH, drive B: 2DH
674 Data
= (UINT8
) (Data
| (SELECT_DRV
& DevPos
));
676 Data
|= DRVA_MOTOR_ON
;
678 Data
|= DRVB_MOTOR_ON
;
681 Info
->MotorOn
= FALSE
;
684 // Turn on the motor and wait for some time to ensure it takes effect.
686 IoWrite8 ((UINT16
) (PcdGet16 (PcdFdcBaseAddress
) + FDC_REGISTER_DOR
), Data
);
687 MicroSecondDelay (FDC_LONG_DELAY
);
689 Info
->MotorOn
= TRUE
;
695 Turn off the motor of floppy drive.
697 @param FdcBlkIoDev Instance of FDC_BLK_IO_DEV.
698 @param Info Information of floppy device.
703 IN FDC_BLK_IO_DEV
*FdcBlkIoDev
,
704 IN OUT PEI_FLOPPY_DEVICE_INFO
*Info
710 DevPos
= Info
->DevPos
;
712 if (!Info
->MotorOn
) {
716 // The motor is on, so need motor off
719 Data
= (UINT8
) (Data
| (SELECT_DRV
& DevPos
));
721 IoWrite8 ((UINT16
) (PcdGet16 (PcdFdcBaseAddress
) + FDC_REGISTER_DOR
), Data
);
722 MicroSecondDelay (FDC_SHORT_DELAY
);
724 Info
->MotorOn
= FALSE
;
728 Recalibrate the FDC device.
730 @param FdcBlkIoDev Instance of FDC_BLK_IO_DEV.
731 @param Info Information of floppy device.
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.
741 IN FDC_BLK_IO_DEV
*FdcBlkIoDev
,
742 IN OUT PEI_FLOPPY_DEVICE_INFO
*Info
745 FDC_COMMAND_PACKET2 Command
;
753 DevPos
= Info
->DevPos
;
756 // We would try twice.
760 ZeroMem (&Command
, sizeof (FDC_COMMAND_PACKET2
));
761 Command
.CommandCode
= RECALIBRATE_CMD
;
766 Command
.DiskHeadSel
= 0;
768 Command
.DiskHeadSel
= 1;
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
;
778 MicroSecondDelay (FDC_RECALIBRATE_DELAY
);
780 if (SenseIntStatus (FdcBlkIoDev
, &Sts0
, &Pcn
) != EFI_SUCCESS
) {
781 return EFI_DEVICE_ERROR
;
784 if ((Sts0
& 0xf0) == BIT5
&& Pcn
== 0) {
786 // Recalibration is successful.
789 Info
->NeedRecalibrate
= FALSE
;
794 // Recalibration is not successful. Try again.
795 // If trial is used out, return EFI_DEVICE_ERROR.
799 return EFI_DEVICE_ERROR
;
808 Seek for the cylinder according to given LBA.
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.
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.
821 IN FDC_BLK_IO_DEV
*FdcBlkIoDev
,
822 IN OUT PEI_FLOPPY_DEVICE_INFO
*Info
,
826 FDC_SEEK_CMD Command
;
827 DISKET_PARA_TABLE
*Para
;
838 DevPos
= Info
->DevPos
;
839 if (Info
->NeedRecalibrate
) {
840 if (Recalibrate (FdcBlkIoDev
, Info
) != EFI_SUCCESS
) {
841 return EFI_DEVICE_ERROR
;
844 // Recalibrate Success
846 Info
->NeedRecalibrate
= FALSE
;
850 // Get the base of disk parameter information corresponding to its type.
852 Para
= (DISKET_PARA_TABLE
*) ((UINT8
*) DiskPara
+ sizeof (DISKET_PARA_TABLE
) * Info
->Type
);
853 EndOfTrack
= Para
->EndOfTrack
;
855 // Calculate cylinder based on Lba and EOT
857 Cylinder
= (UINT8
) ((UINTN
) Lba
/ EndOfTrack
/ 2);
860 // If the dest cylinder is the present cylinder, unnecessary to do the seek operation
862 if (Info
->Pcn
== Cylinder
) {
867 // Calculate the head : 0 or 1
869 Head
= (UINT8
) ((UINTN
) Lba
/ EndOfTrack
% 2);
871 ZeroMem (&Command
, sizeof (FDC_SEEK_CMD
));
872 Command
.CommandCode
= SEEK_CMD
;
874 Command
.DiskHeadSel
= 0;
876 Command
.DiskHeadSel
= 1;
880 // Send command to move to destination cylinder.
882 Command
.DiskHeadSel
= (UINT8
) (Command
.DiskHeadSel
| (Head
<< 2));
883 Command
.NewCylinder
= Cylinder
;
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
;
892 MicroSecondDelay (FDC_SHORT_DELAY
);
895 // Calculate waiting time, which is proportional to the gap between destination
896 // cylinder and present cylinder.
898 if (Info
->Pcn
> Cylinder
) {
899 Gap
= (UINT8
) (Info
->Pcn
- Cylinder
);
901 Gap
= (UINT8
) (Cylinder
- Info
->Pcn
);
904 MicroSecondDelay ((Gap
+ 1) * FDC_LONG_DELAY
);
907 // Confirm if the new cylinder is the destination and status is correct.
909 if (SenseIntStatus (FdcBlkIoDev
, &Sts0
, &Pcn
) != EFI_SUCCESS
) {
910 return EFI_DEVICE_ERROR
;
913 if ((Sts0
& 0xf0) == BIT5
) {
914 Info
->Pcn
= Command
.NewCylinder
;
915 Info
->NeedRecalibrate
= FALSE
;
918 Info
->NeedRecalibrate
= TRUE
;
919 return EFI_DEVICE_ERROR
;
924 Check if diskette is changed.
926 @param FdcBlkIoDev Instance of FDC_BLK_IO_DEV
927 @param Info Information of floppy device.
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.
937 IN FDC_BLK_IO_DEV
*FdcBlkIoDev
,
938 IN OUT PEI_FLOPPY_DEVICE_INFO
*Info
947 Data
= IoRead8 ((UINT16
) (PcdGet16 (PcdFdcBaseAddress
) + FDC_REGISTER_DIR
));
949 MicroSecondDelay (FDC_SHORT_DELAY
);
951 if ((Data
& DIR_DCL
) == DIR_DCL
) {
952 if (Info
->Pcn
!= 0) {
953 Status
= Recalibrate (FdcBlkIoDev
, Info
);
955 Status
= Seek (FdcBlkIoDev
, Info
, 0x30);
958 if (Status
!= EFI_SUCCESS
) {
960 // Fail to do the seek or recalibrate operation
962 return EFI_DEVICE_ERROR
;
965 Data
= IoRead8 ((UINT16
) (PcdGet16 (PcdFdcBaseAddress
) + FDC_REGISTER_DIR
));
967 MicroSecondDelay (FDC_SHORT_DELAY
);
969 if ((Data
& DIR_DCL
) == DIR_DCL
) {
973 return EFI_MEDIA_CHANGED
;
980 Detects if FDC device exists.
982 @param FdcBlkIoDev Instance of FDC_BLK_IO_DEV
983 @param Info Information of floppy device.
984 @param MediaInfo Information of floppy media.
986 @retval TRUE FDC device exists and is working properly.
987 @retval FALSE FDC device does not exist or cannot work properly.
992 IN FDC_BLK_IO_DEV
*FdcBlkIoDev
,
993 IN OUT PEI_FLOPPY_DEVICE_INFO
*Info
,
994 OUT EFI_PEI_BLOCK_IO_MEDIA
*MediaInfo
998 DISKET_PARA_TABLE
*Para
;
1000 Status
= MotorOn (FdcBlkIoDev
, Info
);
1001 if (Status
!= EFI_SUCCESS
) {
1005 Status
= Recalibrate (FdcBlkIoDev
, Info
);
1007 if (Status
!= EFI_SUCCESS
) {
1008 MotorOff (FdcBlkIoDev
, Info
);
1012 // Set Media Parameter
1014 MediaInfo
->DeviceType
= LegacyFloppy
;
1015 MediaInfo
->MediaPresent
= TRUE
;
1020 Status
= DisketChanged (FdcBlkIoDev
, Info
);
1021 if (Status
== EFI_NO_MEDIA
) {
1023 // No diskette in floppy.
1025 MediaInfo
->MediaPresent
= FALSE
;
1026 } else if (Status
!= EFI_MEDIA_CHANGED
&& Status
!= EFI_SUCCESS
) {
1030 MotorOff (FdcBlkIoDev
, Info
);
1034 MotorOff (FdcBlkIoDev
, Info
);
1037 // Get the base of disk parameter information corresponding to its type.
1039 Para
= (DISKET_PARA_TABLE
*) ((UINT8
*) DiskPara
+ sizeof (DISKET_PARA_TABLE
) * Info
->Type
);
1041 MediaInfo
->BlockSize
= BytePerSector
[Para
->Number
];
1042 MediaInfo
->LastBlock
= Para
->EndOfTrack
* 2 * (Para
->MaxTrackNum
+ 1) - 1;
1048 Enumerate floppy device
1050 @param FdcBlkIoDev Instance of floppy device controller
1052 @return Number of FDC devices.
1057 IN FDC_BLK_IO_DEV
*FdcBlkIoDev
1062 EFI_PEI_BLOCK_IO_MEDIA MediaInfo
;
1068 // DevPos=0 means Drive A, 1 means Drive B.
1070 for (DevPos
= 0; DevPos
< 2; DevPos
++) {
1072 // Detecting device presence
1074 REPORT_STATUS_CODE (EFI_PROGRESS_CODE
, EFI_PERIPHERAL_REMOVABLE_MEDIA
+ EFI_P_PC_PRESENCE_DETECT
);
1079 Status
= FdcReset (FdcBlkIoDev
, DevPos
);
1081 if (EFI_ERROR (Status
)) {
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
;
1092 // Discover FDC device
1094 if (DiscoverFdcDevice (FdcBlkIoDev
, &(FdcBlkIoDev
->DeviceInfo
[DevPos
]), &MediaInfo
)) {
1095 FdcBlkIoDev
->DeviceInfo
[DevNo
].DevPos
= DevPos
;
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
;
1103 &(FdcBlkIoDev
->DeviceInfo
[DevNo
].MediaInfo
),
1105 sizeof (EFI_PEI_BLOCK_IO_MEDIA
)
1111 // Assume controller error
1113 REPORT_STATUS_CODE (
1114 EFI_ERROR_CODE
| EFI_ERROR_MINOR
,
1115 EFI_PERIPHERAL_REMOVABLE_MEDIA
+ EFI_P_EC_CONTROLLER_ERROR
1120 FdcBlkIoDev
->DeviceCount
= DevNo
;
1125 Checks result reflected by FDC_RESULT_PACKET.
1127 @param Result FDC_RESULT_PACKET read from FDC after certain operation.
1128 @param Info Information of floppy device.
1130 @retval EFI_SUCCESS Result is healthy.
1131 @retval EFI_DEVICE_ERROR Result is not healthy.
1136 IN FDC_RESULT_PACKET
*Result
,
1137 OUT PEI_FLOPPY_DEVICE_INFO
*Info
1140 if ((Result
->Status0
& STS0_IC
) != IC_NT
) {
1141 if ((Result
->Status0
& STS0_SE
) == BIT5
) {
1145 Info
->NeedRecalibrate
= TRUE
;
1148 Info
->NeedRecalibrate
= TRUE
;
1149 return EFI_DEVICE_ERROR
;
1152 // Check Status Register1
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
;
1159 // Check Status Register2
1161 if ((Result
->Status2
& (STS2_CM
| STS2_DD
| STS2_WC
| STS2_BC
| STS2_MD
)) != 0) {
1162 Info
->NeedRecalibrate
= TRUE
;
1163 return EFI_DEVICE_ERROR
;
1170 Fill parameters for command packet.
1172 @param Info Information of floppy device.
1173 @param Lba Logical block address.
1174 @param Command Command for which for fill parameters.
1179 IN PEI_FLOPPY_DEVICE_INFO
*Info
,
1181 OUT FDC_COMMAND_PACKET1
*Command
1184 DISKET_PARA_TABLE
*Para
;
1188 DevPos
= Info
->DevPos
;
1191 // Get the base of disk parameter information corresponding to its type.
1193 Para
= (DISKET_PARA_TABLE
*) ((UINT8
*) DiskPara
+ sizeof (DISKET_PARA_TABLE
) * Info
->Type
);
1195 EndOfTrack
= Para
->EndOfTrack
;
1198 Command
->DiskHeadSel
= 0;
1200 Command
->DiskHeadSel
= 1;
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
;
1214 Setup specifed FDC device.
1216 @param FdcBlkIoDev Instance of FDC_BLK_IO_DEV.
1217 @param DevPos Index of FDC device.
1219 @retval EFI_SUCCESS FDC device successfully set up.
1220 @retval EFI_DEVICE_ERROR FDC device has errors.
1225 IN FDC_BLK_IO_DEV
*FdcBlkIoDev
,
1231 IoWrite8 ((UINT16
) (PcdGet16 (PcdFdcBaseAddress
) + FDC_REGISTER_CCR
), 0x0);
1233 MicroSecondDelay (FDC_MEDIUM_DELAY
);
1235 Status
= Specify (FdcBlkIoDev
);
1240 Setup DMA channels to read data.
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.
1250 IN FDC_BLK_IO_DEV
*FdcBlkIoDev
,
1253 IN UINTN NumberOfBlocks
1260 // Mask DMA channel 2;
1262 IoWrite8 (R_8237_DMA_WRSMSK_CH0_3
, B_8237_DMA_WRSMSK_CMS
| 2);
1265 // Clear first/last flip flop
1267 IoWrite8 (R_8237_DMA_CBPR_CH0_3
, B_8237_DMA_WRSMSK_CMS
| 2);
1272 IoWrite8 (R_8237_DMA_CHMODE_CH0_3
, V_8237_DMA_CHMODE_SINGLE
| V_8237_DMA_CHMODE_IO2MEM
| 2);
1275 // Set base address and page register
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
);
1282 Data
= (UINT8
) ((UINTN
) Buffer
>> 16);
1283 IoWrite8 (R_8237_DMA_MEM_LP_CH2
, Data
);
1286 // Set count register
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
);
1295 // Clear channel 2 mask
1297 IoWrite8 (R_8237_DMA_WRSMSK_CH0_3
, 0x02);
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.
1305 @param Info Information of floppy device.
1306 @param Lba Start address of block range.
1307 @param NumberOfBlocks Number of blocks of the range.
1309 @return Number of blocks in the same sector.
1313 GetTransferBlockCount (
1314 IN PEI_FLOPPY_DEVICE_INFO
*Info
,
1316 IN UINTN NumberOfBlocks
1319 DISKET_PARA_TABLE
*Para
;
1322 UINT8 SectorsInTrack
;
1325 // Get the base of disk parameter information corresponding to its type.
1327 Para
= (DISKET_PARA_TABLE
*) ((UINT8
*) DiskPara
+ sizeof (DISKET_PARA_TABLE
) * Info
->Type
);
1329 EndOfTrack
= Para
->EndOfTrack
;
1330 Head
= (UINT8
) ((UINTN
) Lba
/ EndOfTrack
% 2);
1332 SectorsInTrack
= (UINT8
) (EndOfTrack
* (2 - Head
) - (UINT8
) ((UINTN
) Lba
% EndOfTrack
));
1333 if (SectorsInTrack
< NumberOfBlocks
) {
1335 // Not all the block range locates in the same sector
1337 return SectorsInTrack
;
1340 // All the block range is in the same sector.
1342 return NumberOfBlocks
;
1347 Read data sector from FDC device.
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.
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.
1362 IN FDC_BLK_IO_DEV
*FdcBlkIoDev
,
1363 IN OUT PEI_FLOPPY_DEVICE_INFO
*Info
,
1366 IN UINTN NumberOfBlocks
1370 FDC_COMMAND_PACKET1 Command
;
1371 FDC_RESULT_PACKET Result
;
1376 Status
= Seek (FdcBlkIoDev
, Info
, Lba
);
1377 if (Status
!= EFI_SUCCESS
) {
1378 return EFI_DEVICE_ERROR
;
1384 SetDMA (FdcBlkIoDev
, Buffer
, Info
->MediaInfo
.BlockSize
, NumberOfBlocks
);
1387 // Allocate Read command packet
1389 ZeroMem (&Command
, sizeof (FDC_COMMAND_PACKET1
));
1390 Command
.CommandCode
= READ_DATA_CMD
| CMD_MT
| CMD_MFM
| CMD_SK
;
1393 // Fill parameters for command.
1395 FillPara (Info
, Lba
, &Command
);
1398 // Write command bytes to FDC
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
;
1408 // Wait for some time until command takes effect.
1410 Times
= (STALL_1_SECOND
/ FDC_CHECK_INTERVAL
) + 1;
1412 if ((IoRead8 ((UINT16
) (PcdGet16 (PcdFdcBaseAddress
) + FDC_REGISTER_MSR
)) & 0xc0) == 0xc0) {
1416 MicroSecondDelay (FDC_SHORT_DELAY
);
1417 } while (--Times
> 0);
1421 // Command fails to take effect in time, return EFI_TIMEOUT.
1427 // Read result bytes from FDC
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
;
1436 return CheckResult (&Result
, Info
);
1440 Gets the count of block I/O devices that one specific block driver detects.
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.
1449 @param[in] PeiServices General-purpose services that are available
1451 @param[in] This Indicates the EFI_PEI_RECOVERY_BLOCK_IO_PPI
1453 @param[out] NumberBlockDevices The number of block I/O devices discovered.
1455 @retval EFI_SUCCESS Operation performed successfully.
1460 FdcGetNumberOfBlockDevices (
1461 IN EFI_PEI_SERVICES
**PeiServices
,
1462 IN EFI_PEI_RECOVERY_BLOCK_IO_PPI
*This
,
1463 OUT UINTN
*NumberBlockDevices
1466 FDC_BLK_IO_DEV
*FdcBlkIoDev
;
1470 FdcBlkIoDev
= PEI_RECOVERY_FDC_FROM_BLKIO_THIS (This
);
1472 *NumberBlockDevices
= FdcBlkIoDev
->DeviceCount
;
1478 Gets a block device's media information.
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.
1484 @param[in] PeiServices General-purpose services that are available to every
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
1494 @param[out] MediaInfo The media information of the specified block media.
1495 The caller is responsible for the ownership of this
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
1502 @retval Others Other failure occurs.
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
1515 FDC_BLK_IO_DEV
*FdcBlkIoDev
;
1521 if (This
== NULL
|| MediaInfo
== NULL
) {
1522 return EFI_INVALID_PARAMETER
;
1525 FdcBlkIoDev
= PEI_RECOVERY_FDC_FROM_BLKIO_THIS (This
);
1527 DeviceCount
= FdcBlkIoDev
->DeviceCount
;
1530 // DeviceIndex is a value from 1 to NumberBlockDevices.
1532 if ((DeviceIndex
< 1) || (DeviceIndex
> DeviceCount
) || (DeviceIndex
> 2)) {
1533 return EFI_INVALID_PARAMETER
;
1536 Index
= DeviceIndex
- 1;
1538 // Probe media and retrieve latest media information
1540 Healthy
= DiscoverFdcDevice (
1542 &FdcBlkIoDev
->DeviceInfo
[Index
],
1547 return EFI_DEVICE_ERROR
;
1551 &(FdcBlkIoDev
->DeviceInfo
[Index
].MediaInfo
),
1553 sizeof (EFI_PEI_BLOCK_IO_MEDIA
)
1560 Reads the requested number of blocks from the specified block device.
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.
1566 @param[in] PeiServices General-purpose services that are available to
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
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
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.
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
,
1604 EFI_PEI_BLOCK_IO_MEDIA MediaInfo
;
1607 UINTN NumberOfBlocks
;
1609 FDC_BLK_IO_DEV
*FdcBlkIoDev
;
1613 ZeroMem (&MediaInfo
, sizeof (EFI_PEI_BLOCK_IO_MEDIA
));
1616 return EFI_INVALID_PARAMETER
;
1619 FdcBlkIoDev
= PEI_RECOVERY_FDC_FROM_BLKIO_THIS (This
);
1621 if (Buffer
== NULL
) {
1622 return EFI_INVALID_PARAMETER
;
1625 Status
= FdcGetBlockDeviceMediaInfo (PeiServices
, This
, DeviceIndex
, &MediaInfo
);
1626 if (Status
!= EFI_SUCCESS
) {
1627 return EFI_DEVICE_ERROR
;
1630 if (!MediaInfo
.MediaPresent
) {
1631 return EFI_NO_MEDIA
;
1634 BlockSize
= MediaInfo
.BlockSize
;
1637 // If BufferSize cannot be divided by block size of FDC device,
1638 // return EFI_BAD_BUFFER_SIZE.
1640 if (BufferSize
% BlockSize
!= 0) {
1641 return EFI_BAD_BUFFER_SIZE
;
1644 NumberOfBlocks
= BufferSize
/ BlockSize
;
1646 if ((StartLBA
+ NumberOfBlocks
- 1) > FdcBlkIoDev
->DeviceInfo
[DeviceIndex
- 1].MediaInfo
.LastBlock
) {
1647 return EFI_INVALID_PARAMETER
;
1650 MemPage
= AllocatePages (EFI_SIZE_TO_PAGES (BufferSize
));
1651 if ((MemPage
== NULL
) || ((UINTN
) MemPage
>= ISA_MAX_MEMORY_ADDRESS
)) {
1653 // If fail to allocate memory under ISA_MAX_MEMORY_ADDRESS, designate the address space for DMA
1655 MemPage
= (VOID
*) ((UINTN
) (UINT32
) 0x0f00000);
1657 Status
= MotorOn (FdcBlkIoDev
, &(FdcBlkIoDev
->DeviceInfo
[DeviceIndex
- 1]));
1658 if (Status
!= EFI_SUCCESS
) {
1659 return EFI_DEVICE_ERROR
;
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
;
1668 // Read data in batches.
1669 // Blocks in the same cylinder are read out in a batch.
1671 while ((Count
= GetTransferBlockCount (
1672 &(FdcBlkIoDev
->DeviceInfo
[DeviceIndex
- 1]),
1675 )) != 0 && Status
== EFI_SUCCESS
) {
1676 Status
= ReadDataSector (
1678 &(FdcBlkIoDev
->DeviceInfo
[DeviceIndex
- 1]),
1683 CopyMem (Buffer
, MemPage
, BlockSize
* Count
);
1685 NumberOfBlocks
-= Count
;
1686 Buffer
= (VOID
*) ((UINTN
) Buffer
+ Count
* BlockSize
);
1689 MotorOff (FdcBlkIoDev
, &(FdcBlkIoDev
->DeviceInfo
[DeviceIndex
- 1]));
1696 FdcReset (FdcBlkIoDev
, FdcBlkIoDev
->DeviceInfo
[DeviceIndex
- 1].DevPos
);
1697 return EFI_DEVICE_ERROR
;
1702 Initializes the floppy disk controller and installs FDC Block I/O PPI.
1704 @param FileHandle Handle of the file being invoked.
1705 @param PeiServices Describes the list of possible PEI Services.
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.
1716 IN EFI_PEI_FILE_HANDLE FileHandle
,
1717 IN CONST EFI_PEI_SERVICES
**PeiServices
1721 FDC_BLK_IO_DEV
*FdcBlkIoDev
;
1725 Status
= PeiServicesRegisterForShadow (FileHandle
);
1726 if (!EFI_ERROR (Status
)) {
1731 // Allocate memory for instance of FDC_BLK_IO_DEV and copy initial value
1732 // from template to it.
1734 FdcBlkIoDev
= AllocatePages (EFI_SIZE_TO_PAGES(sizeof (FDC_BLK_IO_DEV
)));
1735 if (FdcBlkIoDev
== NULL
) {
1736 return EFI_OUT_OF_RESOURCES
;
1738 CopyMem (FdcBlkIoDev
, &mBlockIoDevTemplate
, sizeof (mBlockIoDevTemplate
));
1741 // Initialize DMA controller to enable all channels.
1743 for (Index
= 0; Index
< sizeof (mRegisterTable
) / sizeof (PEI_DMA_TABLE
); Index
++) {
1744 IoWrite8 (mRegisterTable
[Index
].Register
, mRegisterTable
[Index
].Value
);
1746 REPORT_STATUS_CODE (EFI_PROGRESS_CODE
, EFI_PERIPHERAL_REMOVABLE_MEDIA
+ EFI_P_PC_INIT
);
1749 // Enumerate FDC devices.
1751 DeviceCount
= FdcEnumeration (FdcBlkIoDev
);
1752 if (DeviceCount
== 0) {
1753 return EFI_NOT_FOUND
;
1756 FdcBlkIoDev
->PpiDescriptor
.Ppi
= &FdcBlkIoDev
->FdcBlkIo
;
1758 return PeiServicesInstallPpi (&FdcBlkIoDev
->PpiDescriptor
);