2 Floppy Peim to support Recovery function from Floppy device.
4 Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
6 SPDX-License-Identifier: BSD-2-Clause-Patent
11 #include "FloppyPeim.h"
14 PEI_DMA_TABLE mRegisterTable
[] = {
16 // DMA2: Clear Byte Ptr, Enable
19 R_8237_DMA_CBPR_CH4_7
,
23 R_8237_DMA_COMMAND_CH4_7
,
27 // DMA1: Clear Byte Ptr, Enable
30 R_8237_DMA_CBPR_CH0_3
,
34 R_8237_DMA_COMMAND_CH0_3
,
38 // Configure Channel 4 for Cascade Mode
39 // Clear DMA Request and enable DREQ
42 R_8237_DMA_CHMODE_CH4_7
,
43 V_8237_DMA_CHMODE_CASCADE
| 0
50 R_8237_DMA_WRSMSK_CH4_7
,
54 // Configure DMA1 (Channels 0-3) for Single Mode
55 // Clear DMA Request and enable DREQ
58 R_8237_DMA_CHMODE_CH0_3
,
59 V_8237_DMA_CHMODE_SINGLE
| 0
66 R_8237_DMA_WRSMSK_CH0_3
,
70 R_8237_DMA_CHMODE_CH0_3
,
71 V_8237_DMA_CHMODE_SINGLE
| 1
78 R_8237_DMA_WRSMSK_CH0_3
,
82 R_8237_DMA_CHMODE_CH0_3
,
83 V_8237_DMA_CHMODE_SINGLE
| 2
90 R_8237_DMA_WRSMSK_CH0_3
,
94 R_8237_DMA_CHMODE_CH0_3
,
95 V_8237_DMA_CHMODE_SINGLE
| 3
102 R_8237_DMA_WRSMSK_CH0_3
,
106 // Configure DMA2 (Channels 5-7) for Single Mode
107 // Clear DMA Request and enable DREQ
110 R_8237_DMA_CHMODE_CH4_7
,
111 V_8237_DMA_CHMODE_SINGLE
| 1
114 R_8237_DMA_STA_CH4_7
,
118 R_8237_DMA_WRSMSK_CH4_7
,
122 R_8237_DMA_CHMODE_CH4_7
,
123 V_8237_DMA_CHMODE_SINGLE
| 2
126 R_8237_DMA_STA_CH4_7
,
130 R_8237_DMA_WRSMSK_CH4_7
,
134 R_8237_DMA_CHMODE_CH4_7
,
135 V_8237_DMA_CHMODE_SINGLE
| 3
138 R_8237_DMA_STA_CH4_7
,
142 R_8237_DMA_WRSMSK_CH4_7
,
148 // Table of diskette parameters of various diskette types
150 DISKET_PARA_TABLE DiskPara
[9] = {
253 // Byte per sector corresponding to various device types.
255 UINTN BytePerSector
[6] = { 0, 256, 512, 1024, 2048, 4096 };
257 FDC_BLK_IO_DEV mBlockIoDevTemplate
= {
258 FDC_BLK_IO_DEV_SIGNATURE
,
260 FdcGetNumberOfBlockDevices
,
261 FdcGetBlockDeviceMediaInfo
,
265 (EFI_PEI_PPI_DESCRIPTOR_PPI
| EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST
),
266 &gEfiPeiVirtualBlockIoPpiGuid
,
274 Wait and check if bits for DIO and RQM of FDC Main Status Register
275 indicates FDC is ready for read or write.
277 Before writing to FDC or reading from FDC, the Host must examine
278 the bit7(RQM) and bit6(DIO) of the Main Status Register.
280 Command bytes can not be written to Data Register unless RQM is 1 and DIO is 0.
281 Result bytes can not be read from Data Register unless RQM is 1 and DIO is 1.
283 @param FdcBlkIoDev Instance of FDC_BLK_IO_DEV.
284 @param DataIn Indicates data input or output.
287 @param TimeoutInMseconds Timeout value to wait.
289 @retval EFI_SUCCESS FDC is ready.
290 @retval EFI_NOT_READY FDC is not ready within the specified time period.
295 IN FDC_BLK_IO_DEV
*FdcBlkIoDev
,
297 IN UINTN TimeoutInMseconds
301 UINT8 StatusRegister
;
305 // Check bit6 of Main Status Register.
312 Delay
= ((TimeoutInMseconds
* STALL_1_MSECOND
) / FDC_CHECK_INTERVAL
) + 1;
314 StatusRegister
= IoRead8 ((UINT16
) (PcdGet16 (PcdFdcBaseAddress
) + FDC_REGISTER_MSR
));
315 if ((StatusRegister
& MSR_RQM
) == MSR_RQM
&& (StatusRegister
& MSR_DIO
) == BitInOut
) {
322 MicroSecondDelay (FDC_SHORT_DELAY
);
323 } while (--Delay
> 0);
327 // FDC is not ready within the specified time period
329 return EFI_NOT_READY
;
336 Read a byte from FDC data register.
338 @param FdcBlkIoDev Instance of FDC_BLK_IO_DEV.
339 @param Pointer Pointer to buffer to hold data read from FDC.
341 @retval EFI_SUCCESS Byte successfully read.
342 @retval EFI_DEVICE_ERROR FDC is not ready.
347 IN FDC_BLK_IO_DEV
*FdcBlkIoDev
,
354 // Wait for 1ms and detect the FDC is ready to be read
356 if (FdcDRQReady (FdcBlkIoDev
, TRUE
, 1) != EFI_SUCCESS
) {
360 return EFI_DEVICE_ERROR
;
363 Data
= IoRead8 ((UINT16
) (PcdGet16 (PcdFdcBaseAddress
) + FDC_REGISTER_DTR
));
364 MicroSecondDelay (FDC_SHORT_DELAY
);
371 Write a byte to FDC data register.
373 @param FdcBlkIoDev Instance of FDC_BLK_IO_DEV.
374 @param Pointer Pointer to data to write.
376 @retval EFI_SUCCESS Byte successfully written.
377 @retval EFI_DEVICE_ERROR FDC is not ready.
382 IN FDC_BLK_IO_DEV
*FdcBlkIoDev
,
389 // Wait for 1ms and detect the FDC is ready to be written
391 if (FdcDRQReady (FdcBlkIoDev
, FALSE
, 1) != EFI_SUCCESS
) {
395 return EFI_DEVICE_ERROR
;
399 IoWrite8 ((UINT16
) (PcdGet16 (PcdFdcBaseAddress
) + FDC_REGISTER_DTR
), Data
);
400 MicroSecondDelay (FDC_SHORT_DELAY
);
406 Get Sts0 and Pcn status from FDC
408 @param FdcBlkIoDev Instance of FDC_BLK_IO_DEV
409 @param Sts0 Value of Sts0
410 @param Pcn Value of Pcn
412 @retval EFI_SUCCESS Successfully retrieved status value of Sts0 and Pcn.
413 @retval EFI_DEVICE_ERROR Fail to send SENSE_INT_STATUS_CMD.
414 @retval EFI_DEVICE_ERROR Fail to read Sts0.
415 @retval EFI_DEVICE_ERROR Fail to read Pcn.
420 IN FDC_BLK_IO_DEV
*FdcBlkIoDev
,
427 Command
= SENSE_INT_STATUS_CMD
;
429 if (DataOutByte (FdcBlkIoDev
, &Command
) != EFI_SUCCESS
) {
430 return EFI_DEVICE_ERROR
;
433 if (DataInByte (FdcBlkIoDev
, Sts0
) != EFI_SUCCESS
) {
434 return EFI_DEVICE_ERROR
;
437 if (DataInByte (FdcBlkIoDev
, Pcn
) != EFI_SUCCESS
) {
438 return EFI_DEVICE_ERROR
;
445 Issue Specify command.
447 @param FdcBlkIoDev Instance of FDC_BLK_IO_DEV.
449 @retval EFI_SUCCESS Specify command successfully issued.
450 @retval EFI_DEVICE_ERROR FDC device has errors.
455 IN FDC_BLK_IO_DEV
*FdcBlkIoDev
458 FDC_SPECIFY_CMD Command
;
462 ZeroMem (&Command
, sizeof (FDC_SPECIFY_CMD
));
463 Command
.CommandCode
= SPECIFY_CMD
;
467 Command
.SrtHut
= 0xdf;
472 Command
.HltNd
= 0x02;
474 Pointer
= (UINT8
*) (&Command
);
475 for (Index
= 0; Index
< sizeof (FDC_SPECIFY_CMD
); Index
++) {
476 if (DataOutByte (FdcBlkIoDev
, Pointer
++) != EFI_SUCCESS
) {
477 return EFI_DEVICE_ERROR
;
485 Wait until busy bit is cleared.
487 @param FdcBlkIoDev Instance of FDC_BLK_IO_DEV.
488 @param DevPos Position of FDC (Driver A or B)
489 @param TimeoutInMseconds Timeout value to wait.
491 @retval EFI_SUCCESS Busy bit has been cleared before timeout.
492 @retval EFI_TIMEOUT Time goes out before busy bit is cleared.
497 IN FDC_BLK_IO_DEV
*FdcBlkIoDev
,
499 IN UINTN TimeoutInMseconds
503 UINT8 StatusRegister
;
507 // How to determine drive and command are busy or not: by the bits of Main Status Register
508 // bit0: Drive 0 busy (drive A)
509 // bit1: Drive 1 busy (drive B)
510 // bit4: Command busy
512 // set mask: for drive A set bit0 & bit4; for drive B set bit1 & bit4
514 Mask
= (UINT8
) ((DevPos
== 0 ? MSR_DAB
: MSR_DBB
) | MSR_CB
);
516 Delay
= ((TimeoutInMseconds
* STALL_1_MSECOND
) / FDC_CHECK_INTERVAL
) + 1;
519 StatusRegister
= IoRead8 ((UINT16
) (PcdGet16 (PcdFdcBaseAddress
) + FDC_REGISTER_MSR
));
521 if ((StatusRegister
& Mask
) == 0x00) {
528 MicroSecondDelay (FDC_SHORT_DELAY
);
529 } while (--Delay
> 0);
541 @param FdcBlkIoDev Instance of FDC_BLK_IO_DEV
542 @param DevPos Index of FDC device.
544 @retval EFI_SUCCESS FDC device successfully reset.
545 @retval EFI_DEVICE_ERROR Fail to reset FDC device.
550 IN FDC_BLK_IO_DEV
*FdcBlkIoDev
,
560 // Reset specified Floppy Logic Drive according to Fdd -> Disk
561 // Set Digital Output Register(DOR) to do reset work
562 // bit0 & bit1 of DOR : Drive Select
564 // bit3 : DMA and Int bit
565 // Reset : A "0" written to bit2 resets the FDC, this reset will remain active until
566 // a "1" is written to this bit.
568 // use bit0 & bit1 to select the logic drive
572 Data
= (UINT8
) (Data
| (SELECT_DRV
& DevPos
));
573 IoWrite8 ((UINT16
) (PcdGet16 (PcdFdcBaseAddress
) + FDC_REGISTER_DOR
), Data
);
576 // Wait some time, at least 120us.
578 MicroSecondDelay (FDC_RESET_DELAY
);
582 // write "1" to bit3 : enable DMA
585 IoWrite8 ((UINT16
) (PcdGet16 (PcdFdcBaseAddress
) + FDC_REGISTER_DOR
), Data
);
587 MicroSecondDelay (FDC_RESET_DELAY
);
590 // Wait until specified floppy logic drive is not busy
592 if (FdcWaitForBSYClear (FdcBlkIoDev
, DevPos
, 1) != EFI_SUCCESS
) {
593 return EFI_DEVICE_ERROR
;
596 // Set the Transfer Data Rate
598 IoWrite8 ((UINT16
) (PcdGet16 (PcdFdcBaseAddress
) + FDC_REGISTER_CCR
), 0x0);
600 MicroSecondDelay (FDC_MEDIUM_DELAY
);
603 // Issue Sense interrupt command for each drive (totally 4 drives)
605 for (Index
= 0; Index
< 4; Index
++) {
606 if (SenseIntStatus (FdcBlkIoDev
, &Sts0
, &Pcn
) != EFI_SUCCESS
) {
607 return EFI_DEVICE_ERROR
;
611 // Issue Specify command
613 if (Specify (FdcBlkIoDev
) != EFI_SUCCESS
) {
614 return EFI_DEVICE_ERROR
;
621 Turn on the motor of floppy drive.
623 @param FdcBlkIoDev Instance of FDC_BLK_IO_DEV.
624 @param Info Information of floppy device.
626 @retval EFI_SUCCESS Motor is successfully turned on.
627 @retval EFI_SUCCESS Motor is already on.
628 @retval EFI_DEVICE_ERROR Busy bit of FDC cannot be cleared.
633 IN FDC_BLK_IO_DEV
*FdcBlkIoDev
,
634 IN OUT PEI_FLOPPY_DEVICE_INFO
*Info
641 // Control of the floppy drive motors is a big pain. If motor is off, you have to turn it
642 // on first. But you can not leave the motor on all the time, since that would wear out the
643 // disk. On the other hand, if you turn the motor off after each operation, the system performance
644 // will be awful. The compromise used in this driver is to leave the motor on for 2 seconds after
645 // each operation. If a new operation is started in that interval(2s), the motor need not be
646 // turned on again. If no new operation is started, a timer goes off and the motor is turned off.
648 DevPos
= Info
->DevPos
;
651 // If the Motor is already on, just return EFI_SUCCESS.
657 // The drive's motor is off, so need turn it on.
658 // First check if command and drive are busy or not.
660 if (FdcWaitForBSYClear (FdcBlkIoDev
, DevPos
, 1) != EFI_SUCCESS
) {
661 return EFI_DEVICE_ERROR
;
664 // for drive A: 1CH, drive B: 2DH
667 Data
= (UINT8
) (Data
| (SELECT_DRV
& DevPos
));
669 Data
|= DRVA_MOTOR_ON
;
671 Data
|= DRVB_MOTOR_ON
;
674 Info
->MotorOn
= FALSE
;
677 // Turn on the motor and wait for some time to ensure it takes effect.
679 IoWrite8 ((UINT16
) (PcdGet16 (PcdFdcBaseAddress
) + FDC_REGISTER_DOR
), Data
);
680 MicroSecondDelay (FDC_LONG_DELAY
);
682 Info
->MotorOn
= TRUE
;
688 Turn off the motor of floppy drive.
690 @param FdcBlkIoDev Instance of FDC_BLK_IO_DEV.
691 @param Info Information of floppy device.
696 IN FDC_BLK_IO_DEV
*FdcBlkIoDev
,
697 IN OUT PEI_FLOPPY_DEVICE_INFO
*Info
703 DevPos
= Info
->DevPos
;
705 if (!Info
->MotorOn
) {
709 // The motor is on, so need motor off
712 Data
= (UINT8
) (Data
| (SELECT_DRV
& DevPos
));
714 IoWrite8 ((UINT16
) (PcdGet16 (PcdFdcBaseAddress
) + FDC_REGISTER_DOR
), Data
);
715 MicroSecondDelay (FDC_SHORT_DELAY
);
717 Info
->MotorOn
= FALSE
;
721 Recalibrate the FDC device.
723 @param FdcBlkIoDev Instance of FDC_BLK_IO_DEV.
724 @param Info Information of floppy device.
726 @retval EFI_SUCCESS FDC successfully recalibrated.
727 @retval EFI_DEVICE_ERROR Fail to send RECALIBRATE_CMD.
728 @retval EFI_DEVICE_ERROR Fail to get status value of Sts0 and Pcn.
729 @retval EFI_DEVICE_ERROR Fail to recalibrate FDC device.
734 IN FDC_BLK_IO_DEV
*FdcBlkIoDev
,
735 IN OUT PEI_FLOPPY_DEVICE_INFO
*Info
738 FDC_COMMAND_PACKET2 Command
;
746 DevPos
= Info
->DevPos
;
749 // We would try twice.
753 ZeroMem (&Command
, sizeof (FDC_COMMAND_PACKET2
));
754 Command
.CommandCode
= RECALIBRATE_CMD
;
759 Command
.DiskHeadSel
= 0;
761 Command
.DiskHeadSel
= 1;
764 Pointer
= (UINT8
*) (&Command
);
765 for (Index
= 0; Index
< sizeof (FDC_COMMAND_PACKET2
); Index
++) {
766 if (DataOutByte (FdcBlkIoDev
, Pointer
++) != EFI_SUCCESS
) {
767 return EFI_DEVICE_ERROR
;
771 MicroSecondDelay (FDC_RECALIBRATE_DELAY
);
773 if (SenseIntStatus (FdcBlkIoDev
, &Sts0
, &Pcn
) != EFI_SUCCESS
) {
774 return EFI_DEVICE_ERROR
;
777 if ((Sts0
& 0xf0) == BIT5
&& Pcn
== 0) {
779 // Recalibration is successful.
782 Info
->NeedRecalibrate
= FALSE
;
787 // Recalibration is not successful. Try again.
788 // If trial is used out, return EFI_DEVICE_ERROR.
792 return EFI_DEVICE_ERROR
;
801 Seek for the cylinder according to given LBA.
803 @param FdcBlkIoDev Instance of FDC_BLK_IO_DEV.
804 @param Info Information of floppy device.
805 @param Lba LBA for which to seek for cylinder.
807 @retval EFI_SUCCESS Successfully moved to the destination cylinder.
808 @retval EFI_SUCCESS Destination cylinder is just the present cylinder.
809 @retval EFI_DEVICE_ERROR Fail to move to the destination cylinder.
814 IN FDC_BLK_IO_DEV
*FdcBlkIoDev
,
815 IN OUT PEI_FLOPPY_DEVICE_INFO
*Info
,
819 FDC_SEEK_CMD Command
;
820 DISKET_PARA_TABLE
*Para
;
831 DevPos
= Info
->DevPos
;
832 if (Info
->NeedRecalibrate
) {
833 if (Recalibrate (FdcBlkIoDev
, Info
) != EFI_SUCCESS
) {
834 return EFI_DEVICE_ERROR
;
837 // Recalibrate Success
839 Info
->NeedRecalibrate
= FALSE
;
843 // Get the base of disk parameter information corresponding to its type.
845 Para
= (DISKET_PARA_TABLE
*) ((UINT8
*) DiskPara
+ sizeof (DISKET_PARA_TABLE
) * Info
->Type
);
846 EndOfTrack
= Para
->EndOfTrack
;
848 // Calculate cylinder based on Lba and EOT
850 Cylinder
= (UINT8
) ((UINTN
) Lba
/ EndOfTrack
/ 2);
853 // If the dest cylinder is the present cylinder, unnecessary to do the seek operation
855 if (Info
->Pcn
== Cylinder
) {
860 // Calculate the head : 0 or 1
862 Head
= (UINT8
) ((UINTN
) Lba
/ EndOfTrack
% 2);
864 ZeroMem (&Command
, sizeof (FDC_SEEK_CMD
));
865 Command
.CommandCode
= SEEK_CMD
;
867 Command
.DiskHeadSel
= 0;
869 Command
.DiskHeadSel
= 1;
873 // Send command to move to destination cylinder.
875 Command
.DiskHeadSel
= (UINT8
) (Command
.DiskHeadSel
| (Head
<< 2));
876 Command
.NewCylinder
= Cylinder
;
878 Pointer
= (UINT8
*) (&Command
);
879 for (Index
= 0; Index
< sizeof (FDC_SEEK_CMD
); Index
++) {
880 if (DataOutByte (FdcBlkIoDev
, Pointer
++) != EFI_SUCCESS
) {
881 return EFI_DEVICE_ERROR
;
885 MicroSecondDelay (FDC_SHORT_DELAY
);
888 // Calculate waiting time, which is proportional to the gap between destination
889 // cylinder and present cylinder.
891 if (Info
->Pcn
> Cylinder
) {
892 Gap
= (UINT8
) (Info
->Pcn
- Cylinder
);
894 Gap
= (UINT8
) (Cylinder
- Info
->Pcn
);
897 MicroSecondDelay ((Gap
+ 1) * FDC_LONG_DELAY
);
900 // Confirm if the new cylinder is the destination and status is correct.
902 if (SenseIntStatus (FdcBlkIoDev
, &Sts0
, &Pcn
) != EFI_SUCCESS
) {
903 return EFI_DEVICE_ERROR
;
906 if ((Sts0
& 0xf0) == BIT5
) {
907 Info
->Pcn
= Command
.NewCylinder
;
908 Info
->NeedRecalibrate
= FALSE
;
911 Info
->NeedRecalibrate
= TRUE
;
912 return EFI_DEVICE_ERROR
;
917 Check if diskette is changed.
919 @param FdcBlkIoDev Instance of FDC_BLK_IO_DEV
920 @param Info Information of floppy device.
922 @retval EFI_SUCCESS Diskette is not changed.
923 @retval EFI_MEDIA_CHANGED Diskette is changed.
924 @retval EFI_NO_MEDIA No diskette.
925 @retval EFI_DEVICE_ERROR Fail to do the seek or recalibrate operation.
930 IN FDC_BLK_IO_DEV
*FdcBlkIoDev
,
931 IN OUT PEI_FLOPPY_DEVICE_INFO
*Info
940 Data
= IoRead8 ((UINT16
) (PcdGet16 (PcdFdcBaseAddress
) + FDC_REGISTER_DIR
));
942 MicroSecondDelay (FDC_SHORT_DELAY
);
944 if ((Data
& DIR_DCL
) == DIR_DCL
) {
945 if (Info
->Pcn
!= 0) {
946 Status
= Recalibrate (FdcBlkIoDev
, Info
);
948 Status
= Seek (FdcBlkIoDev
, Info
, 0x30);
951 if (Status
!= EFI_SUCCESS
) {
953 // Fail to do the seek or recalibrate operation
955 return EFI_DEVICE_ERROR
;
958 Data
= IoRead8 ((UINT16
) (PcdGet16 (PcdFdcBaseAddress
) + FDC_REGISTER_DIR
));
960 MicroSecondDelay (FDC_SHORT_DELAY
);
962 if ((Data
& DIR_DCL
) == DIR_DCL
) {
966 return EFI_MEDIA_CHANGED
;
973 Detects if FDC device exists.
975 @param FdcBlkIoDev Instance of FDC_BLK_IO_DEV
976 @param Info Information of floppy device.
977 @param MediaInfo Information of floppy media.
979 @retval TRUE FDC device exists and is working properly.
980 @retval FALSE FDC device does not exist or cannot work properly.
985 IN FDC_BLK_IO_DEV
*FdcBlkIoDev
,
986 IN OUT PEI_FLOPPY_DEVICE_INFO
*Info
,
987 OUT EFI_PEI_BLOCK_IO_MEDIA
*MediaInfo
991 DISKET_PARA_TABLE
*Para
;
993 Status
= MotorOn (FdcBlkIoDev
, Info
);
994 if (Status
!= EFI_SUCCESS
) {
998 Status
= Recalibrate (FdcBlkIoDev
, Info
);
1000 if (Status
!= EFI_SUCCESS
) {
1001 MotorOff (FdcBlkIoDev
, Info
);
1005 // Set Media Parameter
1007 MediaInfo
->DeviceType
= LegacyFloppy
;
1008 MediaInfo
->MediaPresent
= TRUE
;
1013 Status
= DisketChanged (FdcBlkIoDev
, Info
);
1014 if (Status
== EFI_NO_MEDIA
) {
1016 // No diskette in floppy.
1018 MediaInfo
->MediaPresent
= FALSE
;
1019 } else if (Status
!= EFI_MEDIA_CHANGED
&& Status
!= EFI_SUCCESS
) {
1023 MotorOff (FdcBlkIoDev
, Info
);
1027 MotorOff (FdcBlkIoDev
, Info
);
1030 // Get the base of disk parameter information corresponding to its type.
1032 Para
= (DISKET_PARA_TABLE
*) ((UINT8
*) DiskPara
+ sizeof (DISKET_PARA_TABLE
) * Info
->Type
);
1034 MediaInfo
->BlockSize
= BytePerSector
[Para
->Number
];
1035 MediaInfo
->LastBlock
= Para
->EndOfTrack
* 2 * (Para
->MaxTrackNum
+ 1) - 1;
1041 Enumerate floppy device
1043 @param FdcBlkIoDev Instance of floppy device controller
1045 @return Number of FDC devices.
1050 IN FDC_BLK_IO_DEV
*FdcBlkIoDev
1055 EFI_PEI_BLOCK_IO_MEDIA MediaInfo
;
1061 // DevPos=0 means Drive A, 1 means Drive B.
1063 for (DevPos
= 0; DevPos
< 2; DevPos
++) {
1065 // Detecting device presence
1067 REPORT_STATUS_CODE (EFI_PROGRESS_CODE
, EFI_PERIPHERAL_REMOVABLE_MEDIA
+ EFI_P_PC_PRESENCE_DETECT
);
1072 Status
= FdcReset (FdcBlkIoDev
, DevPos
);
1074 if (EFI_ERROR (Status
)) {
1078 FdcBlkIoDev
->DeviceInfo
[DevPos
].DevPos
= DevPos
;
1079 FdcBlkIoDev
->DeviceInfo
[DevPos
].Pcn
= 0;
1080 FdcBlkIoDev
->DeviceInfo
[DevPos
].MotorOn
= FALSE
;
1081 FdcBlkIoDev
->DeviceInfo
[DevPos
].NeedRecalibrate
= TRUE
;
1082 FdcBlkIoDev
->DeviceInfo
[DevPos
].Type
= FdcType1440K1440K
;
1085 // Discover FDC device
1087 if (DiscoverFdcDevice (FdcBlkIoDev
, &(FdcBlkIoDev
->DeviceInfo
[DevPos
]), &MediaInfo
)) {
1088 FdcBlkIoDev
->DeviceInfo
[DevNo
].DevPos
= DevPos
;
1090 FdcBlkIoDev
->DeviceInfo
[DevNo
].Pcn
= FdcBlkIoDev
->DeviceInfo
[DevPos
].Pcn
;
1091 FdcBlkIoDev
->DeviceInfo
[DevNo
].MotorOn
= FdcBlkIoDev
->DeviceInfo
[DevPos
].MotorOn
;
1092 FdcBlkIoDev
->DeviceInfo
[DevNo
].NeedRecalibrate
= FdcBlkIoDev
->DeviceInfo
[DevPos
].NeedRecalibrate
;
1093 FdcBlkIoDev
->DeviceInfo
[DevNo
].Type
= FdcBlkIoDev
->DeviceInfo
[DevPos
].Type
;
1096 &(FdcBlkIoDev
->DeviceInfo
[DevNo
].MediaInfo
),
1098 sizeof (EFI_PEI_BLOCK_IO_MEDIA
)
1104 // Assume controller error
1106 REPORT_STATUS_CODE (
1107 EFI_ERROR_CODE
| EFI_ERROR_MINOR
,
1108 EFI_PERIPHERAL_REMOVABLE_MEDIA
+ EFI_P_EC_CONTROLLER_ERROR
1113 FdcBlkIoDev
->DeviceCount
= DevNo
;
1118 Checks result reflected by FDC_RESULT_PACKET.
1120 @param Result FDC_RESULT_PACKET read from FDC after certain operation.
1121 @param Info Information of floppy device.
1123 @retval EFI_SUCCESS Result is healthy.
1124 @retval EFI_DEVICE_ERROR Result is not healthy.
1129 IN FDC_RESULT_PACKET
*Result
,
1130 OUT PEI_FLOPPY_DEVICE_INFO
*Info
1133 if ((Result
->Status0
& STS0_IC
) != IC_NT
) {
1134 if ((Result
->Status0
& STS0_SE
) == BIT5
) {
1138 Info
->NeedRecalibrate
= TRUE
;
1141 Info
->NeedRecalibrate
= TRUE
;
1142 return EFI_DEVICE_ERROR
;
1145 // Check Status Register1
1147 if ((Result
->Status1
& (STS1_EN
| STS1_DE
| STS1_OR
| STS1_ND
| STS1_NW
| STS1_MA
)) != 0) {
1148 Info
->NeedRecalibrate
= TRUE
;
1149 return EFI_DEVICE_ERROR
;
1152 // Check Status Register2
1154 if ((Result
->Status2
& (STS2_CM
| STS2_DD
| STS2_WC
| STS2_BC
| STS2_MD
)) != 0) {
1155 Info
->NeedRecalibrate
= TRUE
;
1156 return EFI_DEVICE_ERROR
;
1163 Fill parameters for command packet.
1165 @param Info Information of floppy device.
1166 @param Lba Logical block address.
1167 @param Command Command for which for fill parameters.
1172 IN PEI_FLOPPY_DEVICE_INFO
*Info
,
1174 OUT FDC_COMMAND_PACKET1
*Command
1177 DISKET_PARA_TABLE
*Para
;
1181 DevPos
= Info
->DevPos
;
1184 // Get the base of disk parameter information corresponding to its type.
1186 Para
= (DISKET_PARA_TABLE
*) ((UINT8
*) DiskPara
+ sizeof (DISKET_PARA_TABLE
) * Info
->Type
);
1188 EndOfTrack
= Para
->EndOfTrack
;
1191 Command
->DiskHeadSel
= 0;
1193 Command
->DiskHeadSel
= 1;
1196 Command
->Cylinder
= (UINT8
) ((UINTN
) Lba
/ EndOfTrack
/ 2);
1197 Command
->Head
= (UINT8
) ((UINTN
) Lba
/ EndOfTrack
% 2);
1198 Command
->Sector
= (UINT8
) ((UINT8
) ((UINTN
) Lba
% EndOfTrack
) + 1);
1199 Command
->DiskHeadSel
= (UINT8
) (Command
->DiskHeadSel
| (Command
->Head
<< 2));
1200 Command
->Number
= Para
->Number
;
1201 Command
->EndOfTrack
= Para
->EndOfTrack
;
1202 Command
->GapLength
= Para
->GapLength
;
1203 Command
->DataLength
= Para
->DataLength
;
1207 Setup specifed FDC device.
1209 @param FdcBlkIoDev Instance of FDC_BLK_IO_DEV.
1210 @param DevPos Index of FDC device.
1212 @retval EFI_SUCCESS FDC device successfully set up.
1213 @retval EFI_DEVICE_ERROR FDC device has errors.
1218 IN FDC_BLK_IO_DEV
*FdcBlkIoDev
,
1224 IoWrite8 ((UINT16
) (PcdGet16 (PcdFdcBaseAddress
) + FDC_REGISTER_CCR
), 0x0);
1226 MicroSecondDelay (FDC_MEDIUM_DELAY
);
1228 Status
= Specify (FdcBlkIoDev
);
1233 Setup DMA channels to read data.
1235 @param FdcBlkIoDev Instance of FDC_BLK_IO_DEV.
1236 @param Buffer Memory buffer for DMA transfer.
1237 @param BlockSize the number of the bytes in one block.
1238 @param NumberOfBlocks Number of blocks to read.
1243 IN FDC_BLK_IO_DEV
*FdcBlkIoDev
,
1246 IN UINTN NumberOfBlocks
1253 // Mask DMA channel 2;
1255 IoWrite8 (R_8237_DMA_WRSMSK_CH0_3
, B_8237_DMA_WRSMSK_CMS
| 2);
1258 // Clear first/last flip flop
1260 IoWrite8 (R_8237_DMA_CBPR_CH0_3
, B_8237_DMA_WRSMSK_CMS
| 2);
1265 IoWrite8 (R_8237_DMA_CHMODE_CH0_3
, V_8237_DMA_CHMODE_SINGLE
| V_8237_DMA_CHMODE_IO2MEM
| 2);
1268 // Set base address and page register
1270 Data
= (UINT8
) (UINTN
) Buffer
;
1271 IoWrite8 (R_8237_DMA_BASE_CA_CH2
, Data
);
1272 Data
= (UINT8
) ((UINTN
) Buffer
>> 8);
1273 IoWrite8 (R_8237_DMA_BASE_CA_CH2
, Data
);
1275 Data
= (UINT8
) ((UINTN
) Buffer
>> 16);
1276 IoWrite8 (R_8237_DMA_MEM_LP_CH2
, Data
);
1279 // Set count register
1281 Count
= BlockSize
* NumberOfBlocks
- 1;
1282 Data
= (UINT8
) (Count
& 0xff);
1283 IoWrite8 (R_8237_DMA_BASE_CC_CH2
, Data
);
1284 Data
= (UINT8
) (Count
>> 8);
1285 IoWrite8 (R_8237_DMA_BASE_CC_CH2
, Data
);
1288 // Clear channel 2 mask
1290 IoWrite8 (R_8237_DMA_WRSMSK_CH0_3
, 0x02);
1295 According to the block range specified by Lba and NumberOfBlocks, calculate
1296 the number of blocks in the same sector, which can be transferred in a batch.
1298 @param Info Information of floppy device.
1299 @param Lba Start address of block range.
1300 @param NumberOfBlocks Number of blocks of the range.
1302 @return Number of blocks in the same sector.
1306 GetTransferBlockCount (
1307 IN PEI_FLOPPY_DEVICE_INFO
*Info
,
1309 IN UINTN NumberOfBlocks
1312 DISKET_PARA_TABLE
*Para
;
1315 UINT8 SectorsInTrack
;
1318 // Get the base of disk parameter information corresponding to its type.
1320 Para
= (DISKET_PARA_TABLE
*) ((UINT8
*) DiskPara
+ sizeof (DISKET_PARA_TABLE
) * Info
->Type
);
1322 EndOfTrack
= Para
->EndOfTrack
;
1323 Head
= (UINT8
) ((UINTN
) Lba
/ EndOfTrack
% 2);
1325 SectorsInTrack
= (UINT8
) (EndOfTrack
* (2 - Head
) - (UINT8
) ((UINTN
) Lba
% EndOfTrack
));
1326 if (SectorsInTrack
< NumberOfBlocks
) {
1328 // Not all the block range locates in the same sector
1330 return SectorsInTrack
;
1333 // All the block range is in the same sector.
1335 return NumberOfBlocks
;
1340 Read data sector from FDC device.
1342 @param FdcBlkIoDev Instance of FDC_BLK_IO_DEV.
1343 @param Info Information of floppy device.
1344 @param Buffer Buffer to setup for DMA.
1345 @param Lba The start address to read.
1346 @param NumberOfBlocks Number of blocks to read.
1348 @retval EFI_SUCCESS Data successfully read out.
1349 @retval EFI_DEVICE_ERROR FDC device has errors.
1350 @retval EFI_TIMEOUT Command does not take effect in time.
1355 IN FDC_BLK_IO_DEV
*FdcBlkIoDev
,
1356 IN OUT PEI_FLOPPY_DEVICE_INFO
*Info
,
1359 IN UINTN NumberOfBlocks
1363 FDC_COMMAND_PACKET1 Command
;
1364 FDC_RESULT_PACKET Result
;
1369 Status
= Seek (FdcBlkIoDev
, Info
, Lba
);
1370 if (Status
!= EFI_SUCCESS
) {
1371 return EFI_DEVICE_ERROR
;
1377 SetDMA (FdcBlkIoDev
, Buffer
, Info
->MediaInfo
.BlockSize
, NumberOfBlocks
);
1380 // Allocate Read command packet
1382 ZeroMem (&Command
, sizeof (FDC_COMMAND_PACKET1
));
1383 Command
.CommandCode
= READ_DATA_CMD
| CMD_MT
| CMD_MFM
| CMD_SK
;
1386 // Fill parameters for command.
1388 FillPara (Info
, Lba
, &Command
);
1391 // Write command bytes to FDC
1393 Pointer
= (UINT8
*) (&Command
);
1394 for (Index
= 0; Index
< sizeof (FDC_COMMAND_PACKET1
); Index
++) {
1395 if (DataOutByte (FdcBlkIoDev
, Pointer
++) != EFI_SUCCESS
) {
1396 return EFI_DEVICE_ERROR
;
1401 // Wait for some time until command takes effect.
1403 Times
= (STALL_1_SECOND
/ FDC_CHECK_INTERVAL
) + 1;
1405 if ((IoRead8 ((UINT16
) (PcdGet16 (PcdFdcBaseAddress
) + FDC_REGISTER_MSR
)) & 0xc0) == 0xc0) {
1409 MicroSecondDelay (FDC_SHORT_DELAY
);
1410 } while (--Times
> 0);
1414 // Command fails to take effect in time, return EFI_TIMEOUT.
1420 // Read result bytes from FDC
1422 Pointer
= (UINT8
*) (&Result
);
1423 for (Index
= 0; Index
< sizeof (FDC_RESULT_PACKET
); Index
++) {
1424 if (DataInByte (FdcBlkIoDev
, Pointer
++) != EFI_SUCCESS
) {
1425 return EFI_DEVICE_ERROR
;
1429 return CheckResult (&Result
, Info
);
1433 Gets the count of block I/O devices that one specific block driver detects.
1435 This function is used for getting the count of block I/O devices that one
1436 specific block driver detects. To the PEI ATAPI driver, it returns the number
1437 of all the detected ATAPI devices it detects during the enumeration process.
1438 To the PEI legacy floppy driver, it returns the number of all the legacy
1439 devices it finds during its enumeration process. If no device is detected,
1440 then the function will return zero.
1442 @param[in] PeiServices General-purpose services that are available
1444 @param[in] This Indicates the EFI_PEI_RECOVERY_BLOCK_IO_PPI
1446 @param[out] NumberBlockDevices The number of block I/O devices discovered.
1448 @retval EFI_SUCCESS Operation performed successfully.
1453 FdcGetNumberOfBlockDevices (
1454 IN EFI_PEI_SERVICES
**PeiServices
,
1455 IN EFI_PEI_RECOVERY_BLOCK_IO_PPI
*This
,
1456 OUT UINTN
*NumberBlockDevices
1459 FDC_BLK_IO_DEV
*FdcBlkIoDev
;
1463 FdcBlkIoDev
= PEI_RECOVERY_FDC_FROM_BLKIO_THIS (This
);
1465 *NumberBlockDevices
= FdcBlkIoDev
->DeviceCount
;
1471 Gets a block device's media information.
1473 This function will provide the caller with the specified block device's media
1474 information. If the media changes, calling this function will update the media
1475 information accordingly.
1477 @param[in] PeiServices General-purpose services that are available to every
1479 @param[in] This Indicates the EFI_PEI_RECOVERY_BLOCK_IO_PPI instance.
1480 @param[in] DeviceIndex Specifies the block device to which the function wants
1481 to talk. Because the driver that implements Block I/O
1482 PPIs will manage multiple block devices, the PPIs that
1483 want to talk to a single device must specify the
1484 device index that was assigned during the enumeration
1485 process. This index is a number from one to
1487 @param[out] MediaInfo The media information of the specified block media.
1488 The caller is responsible for the ownership of this
1491 @retval EFI_SUCCESS Media information about the specified block device
1492 was obtained successfully.
1493 @retval EFI_DEVICE_ERROR Cannot get the media information due to a hardware
1495 @retval Others Other failure occurs.
1500 FdcGetBlockDeviceMediaInfo (
1501 IN EFI_PEI_SERVICES
**PeiServices
,
1502 IN EFI_PEI_RECOVERY_BLOCK_IO_PPI
*This
,
1503 IN UINTN DeviceIndex
,
1504 OUT EFI_PEI_BLOCK_IO_MEDIA
*MediaInfo
1508 FDC_BLK_IO_DEV
*FdcBlkIoDev
;
1514 if (This
== NULL
|| MediaInfo
== NULL
) {
1515 return EFI_INVALID_PARAMETER
;
1518 FdcBlkIoDev
= PEI_RECOVERY_FDC_FROM_BLKIO_THIS (This
);
1520 DeviceCount
= FdcBlkIoDev
->DeviceCount
;
1523 // DeviceIndex is a value from 1 to NumberBlockDevices.
1525 if ((DeviceIndex
< 1) || (DeviceIndex
> DeviceCount
) || (DeviceIndex
> 2)) {
1526 return EFI_INVALID_PARAMETER
;
1529 Index
= DeviceIndex
- 1;
1531 // Probe media and retrieve latest media information
1533 Healthy
= DiscoverFdcDevice (
1535 &FdcBlkIoDev
->DeviceInfo
[Index
],
1540 return EFI_DEVICE_ERROR
;
1544 &(FdcBlkIoDev
->DeviceInfo
[Index
].MediaInfo
),
1546 sizeof (EFI_PEI_BLOCK_IO_MEDIA
)
1553 Reads the requested number of blocks from the specified block device.
1555 The function reads the requested number of blocks from the device. All the
1556 blocks are read, or an error is returned. If there is no media in the device,
1557 the function returns EFI_NO_MEDIA.
1559 @param[in] PeiServices General-purpose services that are available to
1561 @param[in] This Indicates the EFI_PEI_RECOVERY_BLOCK_IO_PPI instance.
1562 @param[in] DeviceIndex Specifies the block device to which the function wants
1563 to talk. Because the driver that implements Block I/O
1564 PPIs will manage multiple block devices, the PPIs that
1565 want to talk to a single device must specify the device
1566 index that was assigned during the enumeration process.
1567 This index is a number from one to NumberBlockDevices.
1568 @param[in] StartLBA The starting logical block address (LBA) to read from
1570 @param[in] BufferSize The size of the Buffer in bytes. This number must be
1571 a multiple of the intrinsic block size of the device.
1572 @param[out] Buffer A pointer to the destination buffer for the data.
1573 The caller is responsible for the ownership of the
1576 @retval EFI_SUCCESS The data was read correctly from the device.
1577 @retval EFI_DEVICE_ERROR The device reported an error while attempting
1578 to perform the read operation.
1579 @retval EFI_INVALID_PARAMETER The read request contains LBAs that are not
1580 valid, or the buffer is not properly aligned.
1581 @retval EFI_NO_MEDIA There is no media in the device.
1582 @retval EFI_BAD_BUFFER_SIZE The BufferSize parameter is not a multiple of
1583 the intrinsic block size of the device.
1589 IN EFI_PEI_SERVICES
**PeiServices
,
1590 IN EFI_PEI_RECOVERY_BLOCK_IO_PPI
*This
,
1591 IN UINTN DeviceIndex
,
1592 IN EFI_PEI_LBA StartLBA
,
1593 IN UINTN BufferSize
,
1597 EFI_PEI_BLOCK_IO_MEDIA MediaInfo
;
1600 UINTN NumberOfBlocks
;
1602 FDC_BLK_IO_DEV
*FdcBlkIoDev
;
1606 ZeroMem (&MediaInfo
, sizeof (EFI_PEI_BLOCK_IO_MEDIA
));
1609 return EFI_INVALID_PARAMETER
;
1612 FdcBlkIoDev
= PEI_RECOVERY_FDC_FROM_BLKIO_THIS (This
);
1614 if (Buffer
== NULL
) {
1615 return EFI_INVALID_PARAMETER
;
1618 Status
= FdcGetBlockDeviceMediaInfo (PeiServices
, This
, DeviceIndex
, &MediaInfo
);
1619 if (Status
!= EFI_SUCCESS
) {
1620 return EFI_DEVICE_ERROR
;
1623 if (!MediaInfo
.MediaPresent
) {
1624 return EFI_NO_MEDIA
;
1627 BlockSize
= MediaInfo
.BlockSize
;
1630 // If BufferSize cannot be divided by block size of FDC device,
1631 // return EFI_BAD_BUFFER_SIZE.
1633 if (BufferSize
% BlockSize
!= 0) {
1634 return EFI_BAD_BUFFER_SIZE
;
1637 NumberOfBlocks
= BufferSize
/ BlockSize
;
1639 if ((StartLBA
+ NumberOfBlocks
- 1) > FdcBlkIoDev
->DeviceInfo
[DeviceIndex
- 1].MediaInfo
.LastBlock
) {
1640 return EFI_INVALID_PARAMETER
;
1643 MemPage
= AllocatePages (EFI_SIZE_TO_PAGES (BufferSize
));
1644 if ((MemPage
== NULL
) || ((UINTN
) MemPage
>= ISA_MAX_MEMORY_ADDRESS
)) {
1646 // If fail to allocate memory under ISA_MAX_MEMORY_ADDRESS, designate the address space for DMA
1648 MemPage
= (VOID
*) ((UINTN
) (UINT32
) 0x0f00000);
1650 Status
= MotorOn (FdcBlkIoDev
, &(FdcBlkIoDev
->DeviceInfo
[DeviceIndex
- 1]));
1651 if (Status
!= EFI_SUCCESS
) {
1652 return EFI_DEVICE_ERROR
;
1655 Status
= Setup (FdcBlkIoDev
, FdcBlkIoDev
->DeviceInfo
[DeviceIndex
- 1].DevPos
);
1656 if (Status
!= EFI_SUCCESS
) {
1657 MotorOff (FdcBlkIoDev
, &(FdcBlkIoDev
->DeviceInfo
[DeviceIndex
- 1]));
1658 return EFI_DEVICE_ERROR
;
1661 // Read data in batches.
1662 // Blocks in the same cylinder are read out in a batch.
1664 while ((Count
= GetTransferBlockCount (
1665 &(FdcBlkIoDev
->DeviceInfo
[DeviceIndex
- 1]),
1668 )) != 0 && Status
== EFI_SUCCESS
) {
1669 Status
= ReadDataSector (
1671 &(FdcBlkIoDev
->DeviceInfo
[DeviceIndex
- 1]),
1676 CopyMem (Buffer
, MemPage
, BlockSize
* Count
);
1678 NumberOfBlocks
-= Count
;
1679 Buffer
= (VOID
*) ((UINTN
) Buffer
+ Count
* BlockSize
);
1682 MotorOff (FdcBlkIoDev
, &(FdcBlkIoDev
->DeviceInfo
[DeviceIndex
- 1]));
1689 FdcReset (FdcBlkIoDev
, FdcBlkIoDev
->DeviceInfo
[DeviceIndex
- 1].DevPos
);
1690 return EFI_DEVICE_ERROR
;
1695 Initializes the floppy disk controller and installs FDC Block I/O PPI.
1697 @param FileHandle Handle of the file being invoked.
1698 @param PeiServices Describes the list of possible PEI Services.
1700 @retval EFI_SUCCESS Successfully initialized FDC and installed PPI.
1701 @retval EFI_NOT_FOUND Cannot find FDC device.
1702 @retval EFI_OUT_OF_RESOURCES Have no enough memory to create instance or descriptors.
1703 @retval Other Fail to install FDC Block I/O PPI.
1709 IN EFI_PEI_FILE_HANDLE FileHandle
,
1710 IN CONST EFI_PEI_SERVICES
**PeiServices
1714 FDC_BLK_IO_DEV
*FdcBlkIoDev
;
1718 Status
= PeiServicesRegisterForShadow (FileHandle
);
1719 if (!EFI_ERROR (Status
)) {
1724 // Allocate memory for instance of FDC_BLK_IO_DEV and copy initial value
1725 // from template to it.
1727 FdcBlkIoDev
= AllocatePages (EFI_SIZE_TO_PAGES(sizeof (FDC_BLK_IO_DEV
)));
1728 if (FdcBlkIoDev
== NULL
) {
1729 return EFI_OUT_OF_RESOURCES
;
1731 CopyMem (FdcBlkIoDev
, &mBlockIoDevTemplate
, sizeof (mBlockIoDevTemplate
));
1734 // Initialize DMA controller to enable all channels.
1736 for (Index
= 0; Index
< sizeof (mRegisterTable
) / sizeof (PEI_DMA_TABLE
); Index
++) {
1737 IoWrite8 (mRegisterTable
[Index
].Register
, mRegisterTable
[Index
].Value
);
1739 REPORT_STATUS_CODE (EFI_PROGRESS_CODE
, EFI_PERIPHERAL_REMOVABLE_MEDIA
+ EFI_P_PC_INIT
);
1742 // Enumerate FDC devices.
1744 DeviceCount
= FdcEnumeration (FdcBlkIoDev
);
1745 if (DeviceCount
== 0) {
1746 return EFI_NOT_FOUND
;
1749 FdcBlkIoDev
->PpiDescriptor
.Ppi
= &FdcBlkIoDev
->FdcBlkIo
;
1751 return PeiServicesInstallPpi (&FdcBlkIoDev
->PpiDescriptor
);