1 /** @file
2 Floppy Peim to support Recovery function from Floppy device.
3 
4 Copyright (c) 2006 - 2014, Intel Corporation. All rights reserved.<BR>
5 
6 This program and the accompanying materials
7 are licensed and made available under the terms and conditions
8 of the BSD License which accompanies this distribution.  The
9 full text of the license may be found at
10 http://opensource.org/licenses/bsd-license.php
11 
12 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
13 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
14 
15 **/
16 
17 
18 #include "FloppyPeim.h"
19 
20 
21 PEI_DMA_TABLE      mRegisterTable[] = {
22   //
23   // DMA2: Clear Byte Ptr, Enable
24   //
25   {
26     R_8237_DMA_CBPR_CH4_7,
27     0
28   },
29   {
30     R_8237_DMA_COMMAND_CH4_7,
31     0
32   },
33   //
34   // DMA1: Clear Byte Ptr, Enable
35   //
36   {
37     R_8237_DMA_CBPR_CH0_3,
38     0
39   },
40   {
41     R_8237_DMA_COMMAND_CH0_3,
42     0
43   },
44   //
45   // Configure Channel 4 for Cascade Mode
46   // Clear DMA Request and enable DREQ
47   //
48   {
49     R_8237_DMA_CHMODE_CH4_7,
50     V_8237_DMA_CHMODE_CASCADE | 0
51   },
52   {
53     R_8237_DMA_STA_CH4_7,
54     0
55   },
56   {
57     R_8237_DMA_WRSMSK_CH4_7,
58     0
59   },
60   //
61   // Configure DMA1 (Channels 0-3) for Single Mode
62   // Clear DMA Request and enable DREQ
63   //
64   {
65     R_8237_DMA_CHMODE_CH0_3,
66     V_8237_DMA_CHMODE_SINGLE | 0
67   },
68   {
69     R_8237_DMA_STA_CH0_3,
70     0
71   },
72   {
73     R_8237_DMA_WRSMSK_CH0_3,
74     0
75   },
76   {
77     R_8237_DMA_CHMODE_CH0_3,
78     V_8237_DMA_CHMODE_SINGLE | 1
79   },
80   {
81     R_8237_DMA_STA_CH0_3,
82     1
83   },
84   {
85     R_8237_DMA_WRSMSK_CH0_3,
86     1
87   },
88   {
89     R_8237_DMA_CHMODE_CH0_3,
90     V_8237_DMA_CHMODE_SINGLE | 2
91   },
92   {
93     R_8237_DMA_STA_CH0_3,
94     2
95   },
96   {
97     R_8237_DMA_WRSMSK_CH0_3,
98     2
99   },
100   {
101     R_8237_DMA_CHMODE_CH0_3,
102     V_8237_DMA_CHMODE_SINGLE | 3
103   },
104   {
105     R_8237_DMA_STA_CH0_3,
106     3
107   },
108   {
109     R_8237_DMA_WRSMSK_CH0_3,
110     3
111   },
112   //
113   // Configure DMA2 (Channels 5-7) for Single Mode
114   // Clear DMA Request and enable DREQ
115   //
116   {
117     R_8237_DMA_CHMODE_CH4_7,
118     V_8237_DMA_CHMODE_SINGLE | 1
119   },
120   {
121     R_8237_DMA_STA_CH4_7,
122     1
123   },
124   {
125     R_8237_DMA_WRSMSK_CH4_7,
126     1
127   },
128   {
129     R_8237_DMA_CHMODE_CH4_7,
130     V_8237_DMA_CHMODE_SINGLE | 2
131   },
132   {
133     R_8237_DMA_STA_CH4_7,
134     2
135   },
136   {
137     R_8237_DMA_WRSMSK_CH4_7,
138     2
139   },
140   {
141     R_8237_DMA_CHMODE_CH4_7,
142     V_8237_DMA_CHMODE_SINGLE | 3
143   },
144   {
145     R_8237_DMA_STA_CH4_7,
146     3
147   },
148   {
149     R_8237_DMA_WRSMSK_CH4_7,
150     3
151   }
152 };
153 
154 //
155 // Table of diskette parameters of various diskette types
156 //
157 DISKET_PARA_TABLE  DiskPara[9] = {
158   {
159     0x09,
160     0x50,
161     0xff,
162     0x2,
163     0x27,
164     0x4,
165     0x25,
166     0x14,
167     0x80
168   },
169   {
170     0x09,
171     0x2a,
172     0xff,
173     0x2,
174     0x27,
175     0x4,
176     0x25,
177     0x0f,
178     0x40
179   },
180   {
181     0x0f,
182     0x54,
183     0xff,
184     0x2,
185     0x4f,
186     0x4,
187     0x25,
188     0x0f,
189     0x0
190   },
191   {
192     0x09,
193     0x50,
194     0xff,
195     0x2,
196     0x4f,
197     0x4,
198     0x25,
199     0x0f,
200     0x80
201   },
202   {
203     0x09,
204     0x2a,
205     0xff,
206     0x2,
207     0x4f,
208     0x4,
209     0x25,
210     0x0f,
211     0x80
212   },
213   {
214     0x12,
215     0x1b,
216     0xff,
217     0x2,
218     0x4f,
219     0x4,
220     0x25,
221     0x0f,
222     0x0
223   },
224   {
225     0x09,
226     0x2a,
227     0xff,
228     0x2,
229     0x4f,
230     0x4,
231     0x25,
232     0x0f,
233     0x80
234   },
235   {
236     0x12,
237     0x1b,
238     0xff,
239     0x2,
240     0x4f,
241     0x4,
242     0x25,
243     0x0f,
244     0x0
245   },
246   {
247     0x24,
248     0x1b,
249     0xff,
250     0x2,
251     0x4f,
252     0x4,
253     0x25,
254     0x0f,
255     0xc0
256   }
257 };
258 
259 //
260 // Byte per sector corresponding to various device types.
261 //
262 UINTN    BytePerSector[6] = { 0, 256, 512, 1024, 2048, 4096 };
263 
264 FDC_BLK_IO_DEV mBlockIoDevTemplate = {
265   FDC_BLK_IO_DEV_SIGNATURE,
266   {
267     FdcGetNumberOfBlockDevices,
268     FdcGetBlockDeviceMediaInfo,
269     FdcReadBlocks,
270   },
271   {
272     (EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST),
273     &gEfiPeiVirtualBlockIoPpiGuid,
274     NULL
275   },
276   0,
277   {{0}}
278 };
279 
280 /**
281   Wait and check if bits for DIO and RQM of FDC Main Status Register
282   indicates FDC is ready for read or write.
283 
284   Before writing to FDC or reading from FDC, the Host must examine
285   the bit7(RQM) and bit6(DIO) of the Main Status Register.
286   That is to say:
287    Command bytes can not be written to Data Register unless RQM is 1 and DIO is 0.
288    Result bytes can not be read from Data Register unless RQM is 1 and DIO is 1.
289 
290   @param  FdcBlkIoDev       Instance of FDC_BLK_IO_DEV.
291   @param  DataIn            Indicates data input or output.
292                             TRUE means input.
293                             FALSE means output.
294   @param  TimeoutInMseconds  Timeout value to wait.
295 
296   @retval EFI_SUCCESS       FDC is ready.
297   @retval EFI_NOT_READY     FDC is not ready within the specified time period.
298 
299 **/
300 EFI_STATUS
FdcDRQReady(IN FDC_BLK_IO_DEV * FdcBlkIoDev,IN BOOLEAN DataIn,IN UINTN TimeoutInMseconds)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
DataInByte(IN FDC_BLK_IO_DEV * FdcBlkIoDev,OUT UINT8 * Pointer)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
DataOutByte(IN FDC_BLK_IO_DEV * FdcBlkIoDev,IN UINT8 * Pointer)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
SenseIntStatus(IN FDC_BLK_IO_DEV * FdcBlkIoDev,OUT UINT8 * Sts0,OUT UINT8 * Pcn)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
Specify(IN FDC_BLK_IO_DEV * FdcBlkIoDev)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
FdcWaitForBSYClear(IN FDC_BLK_IO_DEV * FdcBlkIoDev,IN UINT8 DevPos,IN UINTN TimeoutInMseconds)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
FdcReset(IN FDC_BLK_IO_DEV * FdcBlkIoDev,IN UINT8 DevPos)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
MotorOn(IN FDC_BLK_IO_DEV * FdcBlkIoDev,IN OUT PEI_FLOPPY_DEVICE_INFO * Info)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
MotorOff(IN FDC_BLK_IO_DEV * FdcBlkIoDev,IN OUT PEI_FLOPPY_DEVICE_INFO * Info)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
Recalibrate(IN FDC_BLK_IO_DEV * FdcBlkIoDev,IN OUT PEI_FLOPPY_DEVICE_INFO * Info)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
Seek(IN FDC_BLK_IO_DEV * FdcBlkIoDev,IN OUT PEI_FLOPPY_DEVICE_INFO * Info,IN EFI_PEI_LBA Lba)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
DisketChanged(IN FDC_BLK_IO_DEV * FdcBlkIoDev,IN OUT PEI_FLOPPY_DEVICE_INFO * Info)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
DiscoverFdcDevice(IN FDC_BLK_IO_DEV * FdcBlkIoDev,IN OUT PEI_FLOPPY_DEVICE_INFO * Info,OUT EFI_PEI_BLOCK_IO_MEDIA * MediaInfo)991 DiscoverFdcDevice (
992   IN  FDC_BLK_IO_DEV             *FdcBlkIoDev,
993   IN  OUT PEI_FLOPPY_DEVICE_INFO *Info,
994   OUT EFI_PEI_BLOCK_IO_MEDIA     *MediaInfo
995   )
996 {
997   EFI_STATUS        Status;
998   DISKET_PARA_TABLE *Para;
999 
1000   Status = MotorOn (FdcBlkIoDev, Info);
1001   if (Status != EFI_SUCCESS) {
1002     return FALSE;
1003   }
1004 
1005   Status = Recalibrate (FdcBlkIoDev, Info);
1006 
1007   if (Status != EFI_SUCCESS) {
1008     MotorOff (FdcBlkIoDev, Info);
1009     return FALSE;
1010   }
1011   //
1012   // Set Media Parameter
1013   //
1014   MediaInfo->DeviceType   = LegacyFloppy;
1015   MediaInfo->MediaPresent = TRUE;
1016 
1017   //
1018   // Check Media
1019   //
1020   Status = DisketChanged (FdcBlkIoDev, Info);
1021   if (Status == EFI_NO_MEDIA) {
1022     //
1023     // No diskette in floppy.
1024     //
1025     MediaInfo->MediaPresent = FALSE;
1026   } else if (Status != EFI_MEDIA_CHANGED && Status != EFI_SUCCESS) {
1027     //
1028     // EFI_DEVICE_ERROR
1029     //
1030     MotorOff (FdcBlkIoDev, Info);
1031     return FALSE;
1032   }
1033 
1034   MotorOff (FdcBlkIoDev, Info);
1035 
1036   //
1037   // Get the base of disk parameter information corresponding to its type.
1038   //
1039   Para                  = (DISKET_PARA_TABLE *) ((UINT8 *) DiskPara + sizeof (DISKET_PARA_TABLE) * Info->Type);
1040 
1041   MediaInfo->BlockSize  = BytePerSector[Para->Number];
1042   MediaInfo->LastBlock  = Para->EndOfTrack * 2 * (Para->MaxTrackNum + 1) - 1;
1043 
1044   return TRUE;
1045 }
1046 
1047 /**
1048   Enumerate floppy device
1049 
1050   @param  FdcBlkIoDev  Instance of floppy device controller
1051 
1052   @return Number of FDC devices.
1053 
1054 **/
1055 UINT8
FdcEnumeration(IN FDC_BLK_IO_DEV * FdcBlkIoDev)1056 FdcEnumeration (
1057   IN FDC_BLK_IO_DEV   *FdcBlkIoDev
1058   )
1059 {
1060   UINT8                   DevPos;
1061   UINT8                   DevNo;
1062   EFI_PEI_BLOCK_IO_MEDIA  MediaInfo;
1063   EFI_STATUS              Status;
1064 
1065   DevNo = 0;
1066 
1067   //
1068   // DevPos=0 means Drive A, 1 means Drive B.
1069   //
1070   for (DevPos = 0; DevPos < 2; DevPos++) {
1071     //
1072     // Detecting device presence
1073     //
1074     REPORT_STATUS_CODE (EFI_PROGRESS_CODE, EFI_PERIPHERAL_REMOVABLE_MEDIA + EFI_P_PC_PRESENCE_DETECT);
1075 
1076     //
1077     // Reset FDC
1078     //
1079     Status = FdcReset (FdcBlkIoDev, DevPos);
1080 
1081     if (EFI_ERROR (Status)) {
1082       continue;
1083     }
1084 
1085     FdcBlkIoDev->DeviceInfo[DevPos].DevPos          = DevPos;
1086     FdcBlkIoDev->DeviceInfo[DevPos].Pcn             = 0;
1087     FdcBlkIoDev->DeviceInfo[DevPos].MotorOn         = FALSE;
1088     FdcBlkIoDev->DeviceInfo[DevPos].NeedRecalibrate = TRUE;
1089     FdcBlkIoDev->DeviceInfo[DevPos].Type            = FdcType1440K1440K;
1090 
1091     //
1092     // Discover FDC device
1093     //
1094     if (DiscoverFdcDevice (FdcBlkIoDev, &(FdcBlkIoDev->DeviceInfo[DevPos]), &MediaInfo)) {
1095       FdcBlkIoDev->DeviceInfo[DevNo].DevPos           = DevPos;
1096 
1097       FdcBlkIoDev->DeviceInfo[DevNo].Pcn              = FdcBlkIoDev->DeviceInfo[DevPos].Pcn;
1098       FdcBlkIoDev->DeviceInfo[DevNo].MotorOn          = FdcBlkIoDev->DeviceInfo[DevPos].MotorOn;
1099       FdcBlkIoDev->DeviceInfo[DevNo].NeedRecalibrate  = FdcBlkIoDev->DeviceInfo[DevPos].NeedRecalibrate;
1100       FdcBlkIoDev->DeviceInfo[DevNo].Type             = FdcBlkIoDev->DeviceInfo[DevPos].Type;
1101 
1102       CopyMem (
1103         &(FdcBlkIoDev->DeviceInfo[DevNo].MediaInfo),
1104         &MediaInfo,
1105         sizeof (EFI_PEI_BLOCK_IO_MEDIA)
1106         );
1107 
1108       DevNo++;
1109     } else {
1110       //
1111       // Assume controller error
1112       //
1113       REPORT_STATUS_CODE (
1114         EFI_ERROR_CODE | EFI_ERROR_MINOR,
1115         EFI_PERIPHERAL_REMOVABLE_MEDIA + EFI_P_EC_CONTROLLER_ERROR
1116         );
1117     }
1118   }
1119 
1120   FdcBlkIoDev->DeviceCount = DevNo;
1121   return DevNo;
1122 }
1123 
1124 /**
1125   Checks result reflected by FDC_RESULT_PACKET.
1126 
1127   @param  Result           FDC_RESULT_PACKET read from FDC after certain operation.
1128   @param  Info             Information of floppy device.
1129 
1130   @retval EFI_SUCCESS      Result is healthy.
1131   @retval EFI_DEVICE_ERROR Result is not healthy.
1132 
1133 **/
1134 EFI_STATUS
CheckResult(IN FDC_RESULT_PACKET * Result,OUT PEI_FLOPPY_DEVICE_INFO * Info)1135 CheckResult (
1136   IN  FDC_RESULT_PACKET         *Result,
1137   OUT PEI_FLOPPY_DEVICE_INFO    *Info
1138   )
1139 {
1140   if ((Result->Status0 & STS0_IC) != IC_NT) {
1141     if ((Result->Status0 & STS0_SE) == BIT5) {
1142       //
1143       // Seek error
1144       //
1145       Info->NeedRecalibrate = TRUE;
1146     }
1147 
1148     Info->NeedRecalibrate = TRUE;
1149     return EFI_DEVICE_ERROR;
1150   }
1151   //
1152   // Check Status Register1
1153   //
1154   if ((Result->Status1 & (STS1_EN | STS1_DE | STS1_OR | STS1_ND | STS1_NW | STS1_MA)) != 0) {
1155     Info->NeedRecalibrate = TRUE;
1156     return EFI_DEVICE_ERROR;
1157   }
1158   //
1159   // Check Status Register2
1160   //
1161   if ((Result->Status2 & (STS2_CM | STS2_DD | STS2_WC | STS2_BC | STS2_MD)) != 0) {
1162     Info->NeedRecalibrate = TRUE;
1163     return EFI_DEVICE_ERROR;
1164   }
1165 
1166   return EFI_SUCCESS;
1167 }
1168 
1169 /**
1170   Fill parameters for command packet.
1171 
1172   @param  Info    Information of floppy device.
1173   @param  Lba     Logical block address.
1174   @param  Command Command for which for fill parameters.
1175 
1176 **/
1177 VOID
FillPara(IN PEI_FLOPPY_DEVICE_INFO * Info,IN EFI_PEI_LBA Lba,OUT FDC_COMMAND_PACKET1 * Command)1178 FillPara (
1179   IN  PEI_FLOPPY_DEVICE_INFO *Info,
1180   IN  EFI_PEI_LBA            Lba,
1181   OUT FDC_COMMAND_PACKET1    *Command
1182   )
1183 {
1184   DISKET_PARA_TABLE *Para;
1185   UINT8             EndOfTrack;
1186   UINT8             DevPos;
1187 
1188   DevPos      = Info->DevPos;
1189 
1190   //
1191   // Get the base of disk parameter information corresponding to its type.
1192   //
1193   Para        = (DISKET_PARA_TABLE *) ((UINT8 *) DiskPara + sizeof (DISKET_PARA_TABLE) * Info->Type);
1194 
1195   EndOfTrack  = Para->EndOfTrack;
1196 
1197   if (DevPos == 0) {
1198     Command->DiskHeadSel = 0;
1199   } else {
1200     Command->DiskHeadSel = 1;
1201   }
1202 
1203   Command->Cylinder    = (UINT8) ((UINTN) Lba / EndOfTrack / 2);
1204   Command->Head        = (UINT8) ((UINTN) Lba / EndOfTrack % 2);
1205   Command->Sector      = (UINT8) ((UINT8) ((UINTN) Lba % EndOfTrack) + 1);
1206   Command->DiskHeadSel = (UINT8) (Command->DiskHeadSel | (Command->Head << 2));
1207   Command->Number      = Para->Number;
1208   Command->EndOfTrack  = Para->EndOfTrack;
1209   Command->GapLength   = Para->GapLength;
1210   Command->DataLength  = Para->DataLength;
1211 }
1212 
1213 /**
1214   Setup specifed FDC device.
1215 
1216   @param  FdcBlkIoDev      Instance of FDC_BLK_IO_DEV.
1217   @param  DevPos           Index of FDC device.
1218 
1219   @retval EFI_SUCCESS      FDC device successfully set up.
1220   @retval EFI_DEVICE_ERROR FDC device has errors.
1221 
1222 **/
1223 EFI_STATUS
Setup(IN FDC_BLK_IO_DEV * FdcBlkIoDev,IN UINT8 DevPos)1224 Setup (
1225   IN  FDC_BLK_IO_DEV  *FdcBlkIoDev,
1226   IN  UINT8           DevPos
1227   )
1228 {
1229   EFI_STATUS  Status;
1230 
1231   IoWrite8 ((UINT16) (PcdGet16 (PcdFdcBaseAddress) + FDC_REGISTER_CCR), 0x0);
1232 
1233   MicroSecondDelay (FDC_MEDIUM_DELAY);
1234 
1235   Status = Specify (FdcBlkIoDev);
1236   return Status;
1237 }
1238 
1239 /**
1240   Setup DMA channels to read data.
1241 
1242   @param  FdcBlkIoDev      Instance of FDC_BLK_IO_DEV.
1243   @param  Buffer           Memory buffer for DMA transfer.
1244   @param  BlockSize        the number of the bytes in one block.
1245   @param  NumberOfBlocks   Number of blocks to read.
1246 
1247 **/
1248 VOID
SetDMA(IN FDC_BLK_IO_DEV * FdcBlkIoDev,IN VOID * Buffer,IN UINTN BlockSize,IN UINTN NumberOfBlocks)1249 SetDMA (
1250   IN FDC_BLK_IO_DEV   *FdcBlkIoDev,
1251   IN VOID             *Buffer,
1252   IN UINTN            BlockSize,
1253   IN UINTN            NumberOfBlocks
1254   )
1255 {
1256   UINT8 Data;
1257   UINTN Count;
1258 
1259   //
1260   // Mask DMA channel 2;
1261   //
1262   IoWrite8 (R_8237_DMA_WRSMSK_CH0_3, B_8237_DMA_WRSMSK_CMS | 2);
1263 
1264   //
1265   // Clear first/last flip flop
1266   //
1267   IoWrite8 (R_8237_DMA_CBPR_CH0_3, B_8237_DMA_WRSMSK_CMS | 2);
1268 
1269   //
1270   // Set mode
1271   //
1272   IoWrite8 (R_8237_DMA_CHMODE_CH0_3, V_8237_DMA_CHMODE_SINGLE | V_8237_DMA_CHMODE_IO2MEM | 2);
1273 
1274   //
1275   // Set base address and page register
1276   //
1277   Data = (UINT8) (UINTN) Buffer;
1278   IoWrite8 (R_8237_DMA_BASE_CA_CH2, Data);
1279   Data = (UINT8) ((UINTN) Buffer >> 8);
1280   IoWrite8 (R_8237_DMA_BASE_CA_CH2, Data);
1281 
1282   Data = (UINT8) ((UINTN) Buffer >> 16);
1283   IoWrite8 (R_8237_DMA_MEM_LP_CH2, Data);
1284 
1285   //
1286   // Set count register
1287   //
1288   Count = BlockSize * NumberOfBlocks - 1;
1289   Data  = (UINT8) (Count & 0xff);
1290   IoWrite8 (R_8237_DMA_BASE_CC_CH2, Data);
1291   Data = (UINT8) (Count >> 8);
1292   IoWrite8 (R_8237_DMA_BASE_CC_CH2, Data);
1293 
1294   //
1295   // Clear channel 2 mask
1296   //
1297   IoWrite8 (R_8237_DMA_WRSMSK_CH0_3, 0x02);
1298 }
1299 
1300 
1301 /**
1302   According to the block range specified by Lba and NumberOfBlocks, calculate
1303   the number of blocks in the same sector, which can be transferred in a batch.
1304 
1305   @param  Info           Information of floppy device.
1306   @param  Lba            Start address of block range.
1307   @param  NumberOfBlocks Number of blocks of the range.
1308 
1309   @return Number of blocks in the same sector.
1310 
1311 **/
1312 UINTN
GetTransferBlockCount(IN PEI_FLOPPY_DEVICE_INFO * Info,IN EFI_PEI_LBA Lba,IN UINTN NumberOfBlocks)1313 GetTransferBlockCount (
1314   IN  PEI_FLOPPY_DEVICE_INFO *Info,
1315   IN  EFI_PEI_LBA            Lba,
1316   IN  UINTN                  NumberOfBlocks
1317   )
1318 {
1319   DISKET_PARA_TABLE *Para;
1320   UINT8             EndOfTrack;
1321   UINT8             Head;
1322   UINT8             SectorsInTrack;
1323 
1324   //
1325   // Get the base of disk parameter information corresponding to its type.
1326   //
1327   Para            = (DISKET_PARA_TABLE *) ((UINT8 *) DiskPara + sizeof (DISKET_PARA_TABLE) * Info->Type);
1328 
1329   EndOfTrack      = Para->EndOfTrack;
1330   Head            = (UINT8) ((UINTN) Lba / EndOfTrack % 2);
1331 
1332   SectorsInTrack  = (UINT8) (EndOfTrack * (2 - Head) - (UINT8) ((UINTN) Lba % EndOfTrack));
1333   if (SectorsInTrack < NumberOfBlocks) {
1334     //
1335     // Not all the block range locates in the same sector
1336     //
1337     return SectorsInTrack;
1338   } else {
1339     //
1340     // All the block range is in the same sector.
1341     //
1342     return NumberOfBlocks;
1343   }
1344 }
1345 
1346 /**
1347   Read data sector from FDC device.
1348 
1349   @param  FdcBlkIoDev      Instance of FDC_BLK_IO_DEV.
1350   @param  Info             Information of floppy device.
1351   @param  Buffer           Buffer to setup for DMA.
1352   @param  Lba              The start address to read.
1353   @param  NumberOfBlocks   Number of blocks to read.
1354 
1355   @retval EFI_SUCCESS      Data successfully read out.
1356   @retval EFI_DEVICE_ERROR FDC device has errors.
1357   @retval EFI_TIMEOUT      Command does not take effect in time.
1358 
1359 **/
1360 EFI_STATUS
ReadDataSector(IN FDC_BLK_IO_DEV * FdcBlkIoDev,IN OUT PEI_FLOPPY_DEVICE_INFO * Info,IN VOID * Buffer,IN EFI_PEI_LBA Lba,IN UINTN NumberOfBlocks)1361 ReadDataSector (
1362   IN     FDC_BLK_IO_DEV         *FdcBlkIoDev,
1363   IN OUT PEI_FLOPPY_DEVICE_INFO *Info,
1364   IN     VOID                   *Buffer,
1365   IN     EFI_PEI_LBA            Lba,
1366   IN     UINTN                  NumberOfBlocks
1367   )
1368 {
1369   EFI_STATUS          Status;
1370   FDC_COMMAND_PACKET1 Command;
1371   FDC_RESULT_PACKET   Result;
1372   UINTN               Index;
1373   UINTN               Times;
1374   UINT8               *Pointer;
1375 
1376   Status = Seek (FdcBlkIoDev, Info, Lba);
1377   if (Status != EFI_SUCCESS) {
1378     return EFI_DEVICE_ERROR;
1379   }
1380 
1381   //
1382   // Set up DMA
1383   //
1384   SetDMA (FdcBlkIoDev, Buffer, Info->MediaInfo.BlockSize, NumberOfBlocks);
1385 
1386   //
1387   // Allocate Read command packet
1388   //
1389   ZeroMem (&Command, sizeof (FDC_COMMAND_PACKET1));
1390   Command.CommandCode = READ_DATA_CMD | CMD_MT | CMD_MFM | CMD_SK;
1391 
1392   //
1393   // Fill parameters for command.
1394   //
1395   FillPara (Info, Lba, &Command);
1396 
1397   //
1398   // Write command bytes to FDC
1399   //
1400   Pointer = (UINT8 *) (&Command);
1401   for (Index = 0; Index < sizeof (FDC_COMMAND_PACKET1); Index++) {
1402     if (DataOutByte (FdcBlkIoDev, Pointer++) != EFI_SUCCESS) {
1403       return EFI_DEVICE_ERROR;
1404     }
1405   }
1406 
1407   //
1408   // Wait for some time until command takes effect.
1409   //
1410   Times = (STALL_1_SECOND / FDC_CHECK_INTERVAL) + 1;
1411   do {
1412     if ((IoRead8 ((UINT16) (PcdGet16 (PcdFdcBaseAddress) + FDC_REGISTER_MSR)) & 0xc0) == 0xc0) {
1413       break;
1414     }
1415 
1416     MicroSecondDelay (FDC_SHORT_DELAY);
1417   } while (--Times > 0);
1418 
1419   if (Times == 0) {
1420     //
1421     // Command fails to take effect in time, return EFI_TIMEOUT.
1422     //
1423     return EFI_TIMEOUT;
1424   }
1425 
1426   //
1427   // Read result bytes from FDC
1428   //
1429   Pointer = (UINT8 *) (&Result);
1430   for (Index = 0; Index < sizeof (FDC_RESULT_PACKET); Index++) {
1431     if (DataInByte (FdcBlkIoDev, Pointer++) != EFI_SUCCESS) {
1432       return EFI_DEVICE_ERROR;
1433     }
1434   }
1435 
1436   return CheckResult (&Result, Info);
1437 }
1438 
1439 /**
1440   Gets the count of block I/O devices that one specific block driver detects.
1441 
1442   This function is used for getting the count of block I/O devices that one
1443   specific block driver detects.  To the PEI ATAPI driver, it returns the number
1444   of all the detected ATAPI devices it detects during the enumeration process.
1445   To the PEI legacy floppy driver, it returns the number of all the legacy
1446   devices it finds during its enumeration process. If no device is detected,
1447   then the function will return zero.
1448 
1449   @param[in]  PeiServices          General-purpose services that are available
1450                                    to every PEIM.
1451   @param[in]  This                 Indicates the EFI_PEI_RECOVERY_BLOCK_IO_PPI
1452                                    instance.
1453   @param[out] NumberBlockDevices   The number of block I/O devices discovered.
1454 
1455   @retval     EFI_SUCCESS          Operation performed successfully.
1456 
1457 **/
1458 EFI_STATUS
1459 EFIAPI
FdcGetNumberOfBlockDevices(IN EFI_PEI_SERVICES ** PeiServices,IN EFI_PEI_RECOVERY_BLOCK_IO_PPI * This,OUT UINTN * NumberBlockDevices)1460 FdcGetNumberOfBlockDevices (
1461   IN   EFI_PEI_SERVICES                  **PeiServices,
1462   IN   EFI_PEI_RECOVERY_BLOCK_IO_PPI     *This,
1463   OUT  UINTN                             *NumberBlockDevices
1464   )
1465 {
1466   FDC_BLK_IO_DEV  *FdcBlkIoDev;
1467 
1468   FdcBlkIoDev = NULL;
1469 
1470   FdcBlkIoDev         = PEI_RECOVERY_FDC_FROM_BLKIO_THIS (This);
1471 
1472   *NumberBlockDevices = FdcBlkIoDev->DeviceCount;
1473 
1474   return EFI_SUCCESS;
1475 }
1476 
1477 /**
1478   Gets a block device's media information.
1479 
1480   This function will provide the caller with the specified block device's media
1481   information. If the media changes, calling this function will update the media
1482   information accordingly.
1483 
1484   @param[in]  PeiServices   General-purpose services that are available to every
1485                             PEIM
1486   @param[in]  This          Indicates the EFI_PEI_RECOVERY_BLOCK_IO_PPI instance.
1487   @param[in]  DeviceIndex   Specifies the block device to which the function wants
1488                             to talk. Because the driver that implements Block I/O
1489                             PPIs will manage multiple block devices, the PPIs that
1490                             want to talk to a single device must specify the
1491                             device index that was assigned during the enumeration
1492                             process. This index is a number from one to
1493                             NumberBlockDevices.
1494   @param[out] MediaInfo     The media information of the specified block media.
1495                             The caller is responsible for the ownership of this
1496                             data structure.
1497 
1498   @retval EFI_SUCCESS        Media information about the specified block device
1499                              was obtained successfully.
1500   @retval EFI_DEVICE_ERROR   Cannot get the media information due to a hardware
1501                              error.
1502   @retval Others             Other failure occurs.
1503 
1504 **/
1505 EFI_STATUS
1506 EFIAPI
FdcGetBlockDeviceMediaInfo(IN EFI_PEI_SERVICES ** PeiServices,IN EFI_PEI_RECOVERY_BLOCK_IO_PPI * This,IN UINTN DeviceIndex,OUT EFI_PEI_BLOCK_IO_MEDIA * MediaInfo)1507 FdcGetBlockDeviceMediaInfo (
1508   IN   EFI_PEI_SERVICES                     **PeiServices,
1509   IN   EFI_PEI_RECOVERY_BLOCK_IO_PPI        *This,
1510   IN   UINTN                                DeviceIndex,
1511   OUT  EFI_PEI_BLOCK_IO_MEDIA               *MediaInfo
1512   )
1513 {
1514   UINTN           DeviceCount;
1515   FDC_BLK_IO_DEV  *FdcBlkIoDev;
1516   BOOLEAN         Healthy;
1517   UINTN           Index;
1518 
1519   FdcBlkIoDev = NULL;
1520 
1521   if (This == NULL || MediaInfo == NULL) {
1522     return EFI_INVALID_PARAMETER;
1523   }
1524 
1525   FdcBlkIoDev = PEI_RECOVERY_FDC_FROM_BLKIO_THIS (This);
1526 
1527   DeviceCount = FdcBlkIoDev->DeviceCount;
1528 
1529   //
1530   // DeviceIndex is a value from 1 to NumberBlockDevices.
1531   //
1532   if ((DeviceIndex < 1) || (DeviceIndex > DeviceCount) || (DeviceIndex > 2)) {
1533     return EFI_INVALID_PARAMETER;
1534   }
1535 
1536   Index = DeviceIndex - 1;
1537   //
1538   // Probe media and retrieve latest media information
1539   //
1540   Healthy = DiscoverFdcDevice (
1541               FdcBlkIoDev,
1542               &FdcBlkIoDev->DeviceInfo[Index],
1543               MediaInfo
1544               );
1545 
1546   if (!Healthy) {
1547     return EFI_DEVICE_ERROR;
1548   }
1549 
1550   CopyMem (
1551     &(FdcBlkIoDev->DeviceInfo[Index].MediaInfo),
1552     MediaInfo,
1553     sizeof (EFI_PEI_BLOCK_IO_MEDIA)
1554     );
1555 
1556   return EFI_SUCCESS;
1557 }
1558 
1559 /**
1560   Reads the requested number of blocks from the specified block device.
1561 
1562   The function reads the requested number of blocks from the device. All the
1563   blocks are read, or an error is returned. If there is no media in the device,
1564   the function returns EFI_NO_MEDIA.
1565 
1566   @param[in]  PeiServices   General-purpose services that are available to
1567                             every PEIM.
1568   @param[in]  This          Indicates the EFI_PEI_RECOVERY_BLOCK_IO_PPI instance.
1569   @param[in]  DeviceIndex   Specifies the block device to which the function wants
1570                             to talk. Because the driver that implements Block I/O
1571                             PPIs will manage multiple block devices, the PPIs that
1572                             want to talk to a single device must specify the device
1573                             index that was assigned during the enumeration process.
1574                             This index is a number from one to NumberBlockDevices.
1575   @param[in]  StartLBA      The starting logical block address (LBA) to read from
1576                             on the device
1577   @param[in]  BufferSize    The size of the Buffer in bytes. This number must be
1578                             a multiple of the intrinsic block size of the device.
1579   @param[out] Buffer        A pointer to the destination buffer for the data.
1580                             The caller is responsible for the ownership of the
1581                             buffer.
1582 
1583   @retval EFI_SUCCESS             The data was read correctly from the device.
1584   @retval EFI_DEVICE_ERROR        The device reported an error while attempting
1585                                   to perform the read operation.
1586   @retval EFI_INVALID_PARAMETER   The read request contains LBAs that are not
1587                                   valid, or the buffer is not properly aligned.
1588   @retval EFI_NO_MEDIA            There is no media in the device.
1589   @retval EFI_BAD_BUFFER_SIZE     The BufferSize parameter is not a multiple of
1590                                   the intrinsic block size of the device.
1591 
1592 **/
1593 EFI_STATUS
1594 EFIAPI
FdcReadBlocks(IN EFI_PEI_SERVICES ** PeiServices,IN EFI_PEI_RECOVERY_BLOCK_IO_PPI * This,IN UINTN DeviceIndex,IN EFI_PEI_LBA StartLBA,IN UINTN BufferSize,OUT VOID * Buffer)1595 FdcReadBlocks (
1596   IN   EFI_PEI_SERVICES                  **PeiServices,
1597   IN   EFI_PEI_RECOVERY_BLOCK_IO_PPI     *This,
1598   IN   UINTN                             DeviceIndex,
1599   IN   EFI_PEI_LBA                       StartLBA,
1600   IN   UINTN                             BufferSize,
1601   OUT  VOID                              *Buffer
1602   )
1603 {
1604   EFI_PEI_BLOCK_IO_MEDIA MediaInfo;
1605   EFI_STATUS            Status;
1606   UINTN                 Count;
1607   UINTN                 NumberOfBlocks;
1608   UINTN                 BlockSize;
1609   FDC_BLK_IO_DEV        *FdcBlkIoDev;
1610   VOID                  *MemPage;
1611 
1612   FdcBlkIoDev = NULL;
1613   ZeroMem (&MediaInfo, sizeof (EFI_PEI_BLOCK_IO_MEDIA));
1614 
1615   if (This == NULL) {
1616     return EFI_INVALID_PARAMETER;
1617   }
1618 
1619   FdcBlkIoDev = PEI_RECOVERY_FDC_FROM_BLKIO_THIS (This);
1620 
1621   if (Buffer == NULL) {
1622     return EFI_INVALID_PARAMETER;
1623   }
1624 
1625   Status = FdcGetBlockDeviceMediaInfo (PeiServices, This, DeviceIndex, &MediaInfo);
1626   if (Status != EFI_SUCCESS) {
1627     return EFI_DEVICE_ERROR;
1628   }
1629 
1630   if (!MediaInfo.MediaPresent) {
1631     return EFI_NO_MEDIA;
1632   }
1633 
1634   BlockSize = MediaInfo.BlockSize;
1635 
1636   //
1637   // If BufferSize cannot be divided by block size of FDC device,
1638   // return EFI_BAD_BUFFER_SIZE.
1639   //
1640   if (BufferSize % BlockSize != 0) {
1641     return EFI_BAD_BUFFER_SIZE;
1642   }
1643 
1644   NumberOfBlocks = BufferSize / BlockSize;
1645 
1646   if ((StartLBA + NumberOfBlocks - 1) > FdcBlkIoDev->DeviceInfo[DeviceIndex - 1].MediaInfo.LastBlock) {
1647     return EFI_INVALID_PARAMETER;
1648   }
1649 
1650   MemPage = AllocatePages (EFI_SIZE_TO_PAGES (BufferSize));
1651   if ((MemPage == NULL) || ((UINTN) MemPage >= ISA_MAX_MEMORY_ADDRESS)) {
1652     //
1653     // If fail to allocate memory under ISA_MAX_MEMORY_ADDRESS, designate the address space for DMA
1654     //
1655     MemPage = (VOID *) ((UINTN) (UINT32) 0x0f00000);
1656   }
1657   Status = MotorOn (FdcBlkIoDev, &(FdcBlkIoDev->DeviceInfo[DeviceIndex - 1]));
1658   if (Status != EFI_SUCCESS) {
1659     return EFI_DEVICE_ERROR;
1660   }
1661 
1662   Status = Setup (FdcBlkIoDev, FdcBlkIoDev->DeviceInfo[DeviceIndex - 1].DevPos);
1663   if (Status != EFI_SUCCESS) {
1664     MotorOff (FdcBlkIoDev, &(FdcBlkIoDev->DeviceInfo[DeviceIndex - 1]));
1665     return EFI_DEVICE_ERROR;
1666   }
1667   //
1668   // Read data in batches.
1669   // Blocks in the same cylinder are read out in a batch.
1670   //
1671   while ((Count = GetTransferBlockCount (
1672                     &(FdcBlkIoDev->DeviceInfo[DeviceIndex - 1]),
1673                     StartLBA,
1674                     NumberOfBlocks
1675                     )) != 0 && Status == EFI_SUCCESS) {
1676     Status = ReadDataSector (
1677                FdcBlkIoDev,
1678                &(FdcBlkIoDev->DeviceInfo[DeviceIndex - 1]),
1679                MemPage,
1680                StartLBA,
1681                Count
1682                );
1683     CopyMem (Buffer, MemPage, BlockSize * Count);
1684     StartLBA += Count;
1685     NumberOfBlocks -= Count;
1686     Buffer = (VOID *) ((UINTN) Buffer + Count * BlockSize);
1687   }
1688 
1689   MotorOff (FdcBlkIoDev, &(FdcBlkIoDev->DeviceInfo[DeviceIndex - 1]));
1690 
1691   switch (Status) {
1692   case EFI_SUCCESS:
1693     return EFI_SUCCESS;
1694 
1695   default:
1696     FdcReset (FdcBlkIoDev, FdcBlkIoDev->DeviceInfo[DeviceIndex - 1].DevPos);
1697     return EFI_DEVICE_ERROR;
1698   }
1699 }
1700 
1701 /**
1702   Initializes the floppy disk controller and installs FDC Block I/O PPI.
1703 
1704   @param  FileHandle            Handle of the file being invoked.
1705   @param  PeiServices           Describes the list of possible PEI Services.
1706 
1707   @retval EFI_SUCCESS           Successfully initialized FDC and installed PPI.
1708   @retval EFI_NOT_FOUND         Cannot find FDC device.
1709   @retval EFI_OUT_OF_RESOURCES  Have no enough memory to create instance or descriptors.
1710   @retval Other                 Fail to install FDC Block I/O PPI.
1711 
1712 **/
1713 EFI_STATUS
1714 EFIAPI
FdcPeimEntry(IN EFI_PEI_FILE_HANDLE FileHandle,IN CONST EFI_PEI_SERVICES ** PeiServices)1715 FdcPeimEntry (
1716   IN  EFI_PEI_FILE_HANDLE         FileHandle,
1717   IN  CONST EFI_PEI_SERVICES      **PeiServices
1718   )
1719 {
1720   EFI_STATUS            Status;
1721   FDC_BLK_IO_DEV        *FdcBlkIoDev;
1722   UINTN                 DeviceCount;
1723   UINT32                Index;
1724 
1725   Status = PeiServicesRegisterForShadow (FileHandle);
1726   if (!EFI_ERROR (Status)) {
1727     return Status;
1728   }
1729 
1730   //
1731   // Allocate memory for instance of FDC_BLK_IO_DEV and copy initial value
1732   // from template to it.
1733   //
1734   FdcBlkIoDev = AllocatePages (EFI_SIZE_TO_PAGES(sizeof (FDC_BLK_IO_DEV)));
1735   if (FdcBlkIoDev == NULL) {
1736     return EFI_OUT_OF_RESOURCES;
1737   }
1738   CopyMem (FdcBlkIoDev, &mBlockIoDevTemplate, sizeof (mBlockIoDevTemplate));
1739 
1740   //
1741   // Initialize DMA controller to enable all channels.
1742   //
1743   for (Index = 0; Index < sizeof (mRegisterTable) / sizeof (PEI_DMA_TABLE); Index++) {
1744     IoWrite8 (mRegisterTable[Index].Register, mRegisterTable[Index].Value);
1745   }
1746   REPORT_STATUS_CODE (EFI_PROGRESS_CODE, EFI_PERIPHERAL_REMOVABLE_MEDIA + EFI_P_PC_INIT);
1747 
1748   //
1749   // Enumerate FDC devices.
1750   //
1751   DeviceCount = FdcEnumeration (FdcBlkIoDev);
1752   if (DeviceCount == 0) {
1753     return EFI_NOT_FOUND;
1754   }
1755 
1756   FdcBlkIoDev->PpiDescriptor.Ppi = &FdcBlkIoDev->FdcBlkIo;
1757 
1758   return PeiServicesInstallPpi (&FdcBlkIoDev->PpiDescriptor);
1759 }
1760