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