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