• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /** @file
2   SerialIo implementation for PCI or SIO UARTs.
3 
4 Copyright (c) 2006 - 2016, Intel Corporation. All rights reserved.<BR>
5 This program and the accompanying materials
6 are licensed and made available under the terms and conditions of the BSD License
7 which accompanies this distribution.  The full text of the license may be found at
8 http://opensource.org/licenses/bsd-license.php
9 
10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
12 
13 **/
14 
15 #include "Serial.h"
16 
17 /**
18   Skip the optional Controller device path node and return the
19   pointer to the next device path node.
20 
21   @param DevicePath             Pointer to the device path.
22   @param ContainsControllerNode Returns TRUE if the Controller device path exists.
23   @param ControllerNumber       Returns the Controller Number if Controller device path exists.
24 
25   @return     Pointer to the next device path node.
26 **/
27 UART_DEVICE_PATH *
SkipControllerDevicePathNode(EFI_DEVICE_PATH_PROTOCOL * DevicePath,BOOLEAN * ContainsControllerNode,UINT32 * ControllerNumber)28 SkipControllerDevicePathNode (
29   EFI_DEVICE_PATH_PROTOCOL          *DevicePath,
30   BOOLEAN                           *ContainsControllerNode,
31   UINT32                            *ControllerNumber
32   )
33 {
34   if ((DevicePathType (DevicePath) == HARDWARE_DEVICE_PATH) &&
35       (DevicePathSubType (DevicePath) == HW_CONTROLLER_DP)
36       ) {
37     if (ContainsControllerNode != NULL) {
38       *ContainsControllerNode = TRUE;
39     }
40     if (ControllerNumber != NULL) {
41       *ControllerNumber = ((CONTROLLER_DEVICE_PATH *) DevicePath)->ControllerNumber;
42     }
43     DevicePath = NextDevicePathNode (DevicePath);
44   } else {
45     if (ContainsControllerNode != NULL) {
46       *ContainsControllerNode = FALSE;
47     }
48   }
49   return (UART_DEVICE_PATH *) DevicePath;
50 }
51 
52 /**
53   Checks whether the UART parameters are valid and computes the Divisor.
54 
55   @param  ClockRate      The clock rate of the serial device used to verify
56                          the BaudRate. Do not verify the BaudRate if it's 0.
57   @param  BaudRate       The requested baudrate of the serial device.
58   @param  DataBits       Number of databits used in serial device.
59   @param  Parity         The type of parity used in serial device.
60   @param  StopBits       Number of stopbits used in serial device.
61   @param  Divisor        Return the divisor if ClockRate is not 0.
62   @param  ActualBaudRate Return the actual supported baudrate without
63                          exceeding BaudRate. NULL means baudrate degradation
64                          is not allowed.
65                          If the requested BaudRate is not supported, the routine
66                          returns TRUE and the Actual Baud Rate when ActualBaudRate
67                          is not NULL, returns FALSE when ActualBaudRate is NULL.
68 
69   @retval TRUE   The UART parameters are valid.
70   @retval FALSE  The UART parameters are not valid.
71 **/
72 BOOLEAN
VerifyUartParameters(IN UINT32 ClockRate,IN UINT64 BaudRate,IN UINT8 DataBits,IN EFI_PARITY_TYPE Parity,IN EFI_STOP_BITS_TYPE StopBits,OUT UINT64 * Divisor,OUT UINT64 * ActualBaudRate)73 VerifyUartParameters (
74   IN     UINT32                  ClockRate,
75   IN     UINT64                  BaudRate,
76   IN     UINT8                   DataBits,
77   IN     EFI_PARITY_TYPE         Parity,
78   IN     EFI_STOP_BITS_TYPE      StopBits,
79      OUT UINT64                  *Divisor,
80      OUT UINT64                  *ActualBaudRate
81   )
82 {
83   UINT64                     Remainder;
84   UINT32                     ComputedBaudRate;
85   UINT64                     ComputedDivisor;
86   UINT64                     Percent;
87 
88   if ((DataBits < 5) || (DataBits > 8) ||
89       (Parity < NoParity) || (Parity > SpaceParity) ||
90       (StopBits < OneStopBit) || (StopBits > TwoStopBits) ||
91       ((DataBits == 5) && (StopBits == TwoStopBits)) ||
92       ((DataBits >= 6) && (DataBits <= 8) && (StopBits == OneFiveStopBits))
93       ) {
94     return FALSE;
95   }
96 
97   //
98   // Do not verify the baud rate if clock rate is unknown (0).
99   //
100   if (ClockRate == 0) {
101     return TRUE;
102   }
103 
104   //
105   // Compute divisor use to program the baud rate using a round determination
106   // Divisor = ClockRate / 16 / BaudRate = ClockRate / (16 * BaudRate)
107   //         = ClockRate / (BaudRate << 4)
108   //
109   ComputedDivisor = DivU64x64Remainder (ClockRate, LShiftU64 (BaudRate, 4), &Remainder);
110   //
111   // Round Divisor up by 1 if the Remainder is more than half (16 * BaudRate)
112   // BaudRate * 16 / 2 = BaudRate * 8 = (BaudRate << 3)
113   //
114   if (Remainder >= LShiftU64 (BaudRate, 3)) {
115     ComputedDivisor++;
116   }
117   //
118   // If the computed divisor is larger than the maximum value that can be programmed
119   // into the UART, then the requested baud rate can not be supported.
120   //
121   if (ComputedDivisor > MAX_UINT16) {
122     return FALSE;
123   }
124 
125   //
126   // If the computed divisor is 0, then use a computed divisor of 1, which will select
127   // the maximum supported baud rate.
128   //
129   if (ComputedDivisor == 0) {
130     ComputedDivisor = 1;
131   }
132 
133   //
134   // Actual baud rate that the serial port will be programmed for
135   // should be with in 4% of requested one.
136   //
137   ComputedBaudRate = ClockRate / ((UINT16) ComputedDivisor << 4);
138   if (ComputedBaudRate == 0) {
139     return FALSE;
140   }
141 
142   Percent = DivU64x32 (MultU64x32 (BaudRate, 100), ComputedBaudRate);
143   DEBUG ((EFI_D_INFO, "ClockRate = %d\n",  ClockRate));
144   DEBUG ((EFI_D_INFO, "Divisor   = %ld\n", ComputedDivisor));
145   DEBUG ((EFI_D_INFO, "BaudRate/Actual (%ld/%d) = %d%%\n", BaudRate, ComputedBaudRate, Percent));
146 
147   //
148   // If the requested BaudRate is not supported:
149   //  Returns TRUE and the Actual Baud Rate when ActualBaudRate is not NULL;
150   //  Returns FALSE when ActualBaudRate is NULL.
151   //
152   if ((Percent >= 96) && (Percent <= 104)) {
153     if (ActualBaudRate != NULL) {
154       *ActualBaudRate = BaudRate;
155     }
156     if (Divisor != NULL) {
157       *Divisor = ComputedDivisor;
158     }
159     return TRUE;
160   }
161   if (ComputedBaudRate < BaudRate) {
162     if (ActualBaudRate != NULL) {
163       *ActualBaudRate = ComputedBaudRate;
164     }
165     if (Divisor != NULL) {
166       *Divisor = ComputedDivisor;
167     }
168     return TRUE;
169   }
170 
171   //
172   // ActualBaudRate is higher than requested baud rate and more than 4%
173   // higher than the requested value.  Increment Divisor if it is less
174   // than MAX_UINT16 and computed baud rate with new divisor.
175   //
176   if (ComputedDivisor == MAX_UINT16) {
177     return FALSE;
178   }
179   ComputedDivisor++;
180   ComputedBaudRate = ClockRate / ((UINT16) ComputedDivisor << 4);
181   if (ComputedBaudRate == 0) {
182     return FALSE;
183   }
184 
185   DEBUG ((EFI_D_INFO, "ClockRate = %d\n",  ClockRate));
186   DEBUG ((EFI_D_INFO, "Divisor   = %ld\n", ComputedDivisor));
187   DEBUG ((EFI_D_INFO, "BaudRate/Actual (%ld/%d) = %d%%\n", BaudRate, ComputedBaudRate, Percent));
188 
189   if (ActualBaudRate != NULL) {
190     *ActualBaudRate = ComputedBaudRate;
191   }
192   if (Divisor != NULL) {
193     *Divisor = ComputedDivisor;
194   }
195   return TRUE;
196 }
197 
198 /**
199   Detect whether specific FIFO is full or not.
200 
201   @param Fifo    A pointer to the Data Structure SERIAL_DEV_FIFO
202 
203   @return whether specific FIFO is full or not
204 **/
205 BOOLEAN
SerialFifoFull(IN SERIAL_DEV_FIFO * Fifo)206 SerialFifoFull (
207   IN SERIAL_DEV_FIFO *Fifo
208   )
209 {
210   return (BOOLEAN) (((Fifo->Tail + 1) % SERIAL_MAX_FIFO_SIZE) == Fifo->Head);
211 }
212 
213 /**
214   Detect whether specific FIFO is empty or not.
215 
216   @param  Fifo    A pointer to the Data Structure SERIAL_DEV_FIFO
217 
218   @return whether specific FIFO is empty or not
219 **/
220 BOOLEAN
SerialFifoEmpty(IN SERIAL_DEV_FIFO * Fifo)221 SerialFifoEmpty (
222   IN SERIAL_DEV_FIFO *Fifo
223   )
224 
225 {
226   return (BOOLEAN) (Fifo->Head == Fifo->Tail);
227 }
228 
229 /**
230   Add data to specific FIFO.
231 
232   @param Fifo                  A pointer to the Data Structure SERIAL_DEV_FIFO
233   @param Data                  the data added to FIFO
234 
235   @retval EFI_SUCCESS           Add data to specific FIFO successfully
236   @retval EFI_OUT_OF_RESOURCE   Failed to add data because FIFO is already full
237 **/
238 EFI_STATUS
SerialFifoAdd(IN OUT SERIAL_DEV_FIFO * Fifo,IN UINT8 Data)239 SerialFifoAdd (
240   IN OUT SERIAL_DEV_FIFO *Fifo,
241   IN     UINT8           Data
242   )
243 {
244   //
245   // if FIFO full can not add data
246   //
247   if (SerialFifoFull (Fifo)) {
248     return EFI_OUT_OF_RESOURCES;
249   }
250   //
251   // FIFO is not full can add data
252   //
253   Fifo->Data[Fifo->Tail] = Data;
254   Fifo->Tail = (Fifo->Tail + 1) % SERIAL_MAX_FIFO_SIZE;
255   return EFI_SUCCESS;
256 }
257 
258 /**
259   Remove data from specific FIFO.
260 
261   @param Fifo                  A pointer to the Data Structure SERIAL_DEV_FIFO
262   @param Data                  the data removed from FIFO
263 
264   @retval EFI_SUCCESS           Remove data from specific FIFO successfully
265   @retval EFI_OUT_OF_RESOURCE   Failed to remove data because FIFO is empty
266 
267 **/
268 EFI_STATUS
SerialFifoRemove(IN OUT SERIAL_DEV_FIFO * Fifo,OUT UINT8 * Data)269 SerialFifoRemove (
270   IN OUT SERIAL_DEV_FIFO *Fifo,
271   OUT    UINT8           *Data
272   )
273 {
274   //
275   // if FIFO is empty, no data can remove
276   //
277   if (SerialFifoEmpty (Fifo)) {
278     return EFI_OUT_OF_RESOURCES;
279   }
280   //
281   // FIFO is not empty, can remove data
282   //
283   *Data = Fifo->Data[Fifo->Head];
284   Fifo->Head = (Fifo->Head + 1) % SERIAL_MAX_FIFO_SIZE;
285   return EFI_SUCCESS;
286 }
287 
288 /**
289   Reads and writes all available data.
290 
291   @param SerialDevice           The device to transmit.
292 
293   @retval EFI_SUCCESS           Data was read/written successfully.
294   @retval EFI_OUT_OF_RESOURCE   Failed because software receive FIFO is full.  Note, when
295                                 this happens, pending writes are not done.
296 
297 **/
298 EFI_STATUS
SerialReceiveTransmit(IN SERIAL_DEV * SerialDevice)299 SerialReceiveTransmit (
300   IN SERIAL_DEV *SerialDevice
301   )
302 
303 {
304   SERIAL_PORT_LSR Lsr;
305   UINT8           Data;
306   BOOLEAN         ReceiveFifoFull;
307   SERIAL_PORT_MSR Msr;
308   SERIAL_PORT_MCR Mcr;
309   UINTN           TimeOut;
310 
311   Data = 0;
312 
313   //
314   // Begin the read or write
315   //
316   if (SerialDevice->SoftwareLoopbackEnable) {
317     do {
318       ReceiveFifoFull = SerialFifoFull (&SerialDevice->Receive);
319       if (!SerialFifoEmpty (&SerialDevice->Transmit)) {
320         SerialFifoRemove (&SerialDevice->Transmit, &Data);
321         if (ReceiveFifoFull) {
322           return EFI_OUT_OF_RESOURCES;
323         }
324 
325         SerialFifoAdd (&SerialDevice->Receive, Data);
326       }
327     } while (!SerialFifoEmpty (&SerialDevice->Transmit));
328   } else {
329     ReceiveFifoFull = SerialFifoFull (&SerialDevice->Receive);
330     //
331     // For full handshake flow control, tell the peer to send data
332     // if receive buffer is available.
333     //
334     if (SerialDevice->HardwareFlowControl &&
335         !FeaturePcdGet(PcdSerialUseHalfHandshake)&&
336         !ReceiveFifoFull
337         ) {
338       Mcr.Data     = READ_MCR (SerialDevice);
339       Mcr.Bits.Rts = 1;
340       WRITE_MCR (SerialDevice, Mcr.Data);
341     }
342     do {
343       Lsr.Data = READ_LSR (SerialDevice);
344 
345       //
346       // Flush incomming data to prevent a an overrun during a long write
347       //
348       if ((Lsr.Bits.Dr == 1) && !ReceiveFifoFull) {
349         ReceiveFifoFull = SerialFifoFull (&SerialDevice->Receive);
350         if (!ReceiveFifoFull) {
351           if (Lsr.Bits.FIFOe == 1 || Lsr.Bits.Oe == 1 || Lsr.Bits.Pe == 1 || Lsr.Bits.Fe == 1 || Lsr.Bits.Bi == 1) {
352             REPORT_STATUS_CODE_WITH_DEVICE_PATH (
353               EFI_ERROR_CODE,
354               EFI_P_EC_INPUT_ERROR | EFI_PERIPHERAL_SERIAL_PORT,
355               SerialDevice->DevicePath
356               );
357             if (Lsr.Bits.FIFOe == 1 || Lsr.Bits.Pe == 1|| Lsr.Bits.Fe == 1 || Lsr.Bits.Bi == 1) {
358               Data = READ_RBR (SerialDevice);
359               continue;
360             }
361           }
362 
363           Data = READ_RBR (SerialDevice);
364 
365           SerialFifoAdd (&SerialDevice->Receive, Data);
366 
367           //
368           // For full handshake flow control, if receive buffer full
369           // tell the peer to stop sending data.
370           //
371           if (SerialDevice->HardwareFlowControl &&
372               !FeaturePcdGet(PcdSerialUseHalfHandshake)   &&
373               SerialFifoFull (&SerialDevice->Receive)
374               ) {
375             Mcr.Data     = READ_MCR (SerialDevice);
376             Mcr.Bits.Rts = 0;
377             WRITE_MCR (SerialDevice, Mcr.Data);
378           }
379 
380 
381           continue;
382         } else {
383           REPORT_STATUS_CODE_WITH_DEVICE_PATH (
384             EFI_PROGRESS_CODE,
385             EFI_P_SERIAL_PORT_PC_CLEAR_BUFFER | EFI_PERIPHERAL_SERIAL_PORT,
386             SerialDevice->DevicePath
387             );
388         }
389       }
390       //
391       // Do the write
392       //
393       if (Lsr.Bits.Thre == 1 && !SerialFifoEmpty (&SerialDevice->Transmit)) {
394         //
395         // Make sure the transmit data will not be missed
396         //
397         if (SerialDevice->HardwareFlowControl) {
398           //
399           // For half handshake flow control assert RTS before sending.
400           //
401           if (FeaturePcdGet(PcdSerialUseHalfHandshake)) {
402             Mcr.Data     = READ_MCR (SerialDevice);
403             Mcr.Bits.Rts= 0;
404             WRITE_MCR (SerialDevice, Mcr.Data);
405           }
406           //
407           // Wait for CTS
408           //
409           TimeOut   = 0;
410           Msr.Data  = READ_MSR (SerialDevice);
411           while ((Msr.Bits.Dcd == 1) && ((Msr.Bits.Cts == 0) ^ FeaturePcdGet(PcdSerialUseHalfHandshake))) {
412             gBS->Stall (TIMEOUT_STALL_INTERVAL);
413             TimeOut++;
414             if (TimeOut > 5) {
415               break;
416             }
417 
418             Msr.Data = READ_MSR (SerialDevice);
419           }
420 
421           if ((Msr.Bits.Dcd == 0) || ((Msr.Bits.Cts == 1) ^ FeaturePcdGet(PcdSerialUseHalfHandshake))) {
422             SerialFifoRemove (&SerialDevice->Transmit, &Data);
423             WRITE_THR (SerialDevice, Data);
424           }
425 
426           //
427           // For half handshake flow control, tell DCE we are done.
428           //
429           if (FeaturePcdGet(PcdSerialUseHalfHandshake)) {
430             Mcr.Data = READ_MCR (SerialDevice);
431             Mcr.Bits.Rts = 1;
432             WRITE_MCR (SerialDevice, Mcr.Data);
433           }
434         } else {
435           SerialFifoRemove (&SerialDevice->Transmit, &Data);
436           WRITE_THR (SerialDevice, Data);
437         }
438       }
439     } while (Lsr.Bits.Thre == 1 && !SerialFifoEmpty (&SerialDevice->Transmit));
440   }
441 
442   return EFI_SUCCESS;
443 }
444 
445 //
446 // Interface Functions
447 //
448 /**
449   Reset serial device.
450 
451   @param This               Pointer to EFI_SERIAL_IO_PROTOCOL
452 
453   @retval EFI_SUCCESS        Reset successfully
454   @retval EFI_DEVICE_ERROR   Failed to reset
455 
456 **/
457 EFI_STATUS
458 EFIAPI
SerialReset(IN EFI_SERIAL_IO_PROTOCOL * This)459 SerialReset (
460   IN EFI_SERIAL_IO_PROTOCOL  *This
461   )
462 {
463   EFI_STATUS      Status;
464   SERIAL_DEV      *SerialDevice;
465   SERIAL_PORT_LCR Lcr;
466   SERIAL_PORT_IER Ier;
467   SERIAL_PORT_MCR Mcr;
468   SERIAL_PORT_FCR Fcr;
469   EFI_TPL         Tpl;
470   UINT32          Control;
471 
472   SerialDevice = SERIAL_DEV_FROM_THIS (This);
473 
474   //
475   // Report the status code reset the serial
476   //
477   REPORT_STATUS_CODE_WITH_DEVICE_PATH (
478     EFI_PROGRESS_CODE,
479     EFI_P_PC_RESET | EFI_PERIPHERAL_SERIAL_PORT,
480     SerialDevice->DevicePath
481     );
482 
483   Tpl = gBS->RaiseTPL (TPL_NOTIFY);
484 
485   //
486   // Make sure DLAB is 0.
487   //
488   Lcr.Data      = READ_LCR (SerialDevice);
489   Lcr.Bits.DLab = 0;
490   WRITE_LCR (SerialDevice, Lcr.Data);
491 
492   //
493   // Turn off all interrupts
494   //
495   Ier.Data        = READ_IER (SerialDevice);
496   Ier.Bits.Ravie  = 0;
497   Ier.Bits.Theie  = 0;
498   Ier.Bits.Rie    = 0;
499   Ier.Bits.Mie    = 0;
500   WRITE_IER (SerialDevice, Ier.Data);
501 
502   //
503   // Reset the FIFO
504   //
505   Fcr.Data = 0;
506   Fcr.Bits.TrFIFOE = 0;
507   WRITE_FCR (SerialDevice, Fcr.Data);
508 
509   //
510   // Turn off loopback and disable device interrupt.
511   //
512   Mcr.Data      = READ_MCR (SerialDevice);
513   Mcr.Bits.Out1 = 0;
514   Mcr.Bits.Out2 = 0;
515   Mcr.Bits.Lme  = 0;
516   WRITE_MCR (SerialDevice, Mcr.Data);
517 
518   //
519   // Clear the scratch pad register
520   //
521   WRITE_SCR (SerialDevice, 0);
522 
523   //
524   // Enable FIFO
525   //
526   Fcr.Bits.TrFIFOE  = 1;
527   if (SerialDevice->ReceiveFifoDepth > 16) {
528     Fcr.Bits.TrFIFO64 = 1;
529   }
530   Fcr.Bits.ResetRF  = 1;
531   Fcr.Bits.ResetTF  = 1;
532   WRITE_FCR (SerialDevice, Fcr.Data);
533 
534   //
535   // Go set the current attributes
536   //
537   Status = This->SetAttributes (
538                    This,
539                    This->Mode->BaudRate,
540                    This->Mode->ReceiveFifoDepth,
541                    This->Mode->Timeout,
542                    (EFI_PARITY_TYPE) This->Mode->Parity,
543                    (UINT8) This->Mode->DataBits,
544                    (EFI_STOP_BITS_TYPE) This->Mode->StopBits
545                    );
546 
547   if (EFI_ERROR (Status)) {
548     gBS->RestoreTPL (Tpl);
549     return EFI_DEVICE_ERROR;
550   }
551   //
552   // Go set the current control bits
553   //
554   Control = 0;
555   if (SerialDevice->HardwareFlowControl) {
556     Control |= EFI_SERIAL_HARDWARE_FLOW_CONTROL_ENABLE;
557   }
558   if (SerialDevice->SoftwareLoopbackEnable) {
559     Control |= EFI_SERIAL_SOFTWARE_LOOPBACK_ENABLE;
560   }
561   Status = This->SetControl (
562                    This,
563                    Control
564                    );
565 
566   if (EFI_ERROR (Status)) {
567     gBS->RestoreTPL (Tpl);
568     return EFI_DEVICE_ERROR;
569   }
570 
571   //
572   // Reset the software FIFO
573   //
574   SerialDevice->Receive.Head = SerialDevice->Receive.Tail = 0;
575   SerialDevice->Transmit.Head = SerialDevice->Transmit.Tail = 0;
576   gBS->RestoreTPL (Tpl);
577 
578   //
579   // Device reset is complete
580   //
581   return EFI_SUCCESS;
582 }
583 
584 /**
585   Set new attributes to a serial device.
586 
587   @param This                     Pointer to EFI_SERIAL_IO_PROTOCOL
588   @param  BaudRate                 The baudrate of the serial device
589   @param  ReceiveFifoDepth         The depth of receive FIFO buffer
590   @param  Timeout                  The request timeout for a single char
591   @param  Parity                   The type of parity used in serial device
592   @param  DataBits                 Number of databits used in serial device
593   @param  StopBits                 Number of stopbits used in serial device
594 
595   @retval  EFI_SUCCESS              The new attributes were set
596   @retval  EFI_INVALID_PARAMETERS   One or more attributes have an unsupported value
597   @retval  EFI_UNSUPPORTED          Data Bits can not set to 5 or 6
598   @retval  EFI_DEVICE_ERROR         The serial device is not functioning correctly (no return)
599 
600 **/
601 EFI_STATUS
602 EFIAPI
SerialSetAttributes(IN EFI_SERIAL_IO_PROTOCOL * This,IN UINT64 BaudRate,IN UINT32 ReceiveFifoDepth,IN UINT32 Timeout,IN EFI_PARITY_TYPE Parity,IN UINT8 DataBits,IN EFI_STOP_BITS_TYPE StopBits)603 SerialSetAttributes (
604   IN EFI_SERIAL_IO_PROTOCOL  *This,
605   IN UINT64                  BaudRate,
606   IN UINT32                  ReceiveFifoDepth,
607   IN UINT32                  Timeout,
608   IN EFI_PARITY_TYPE         Parity,
609   IN UINT8                   DataBits,
610   IN EFI_STOP_BITS_TYPE      StopBits
611   )
612 {
613   EFI_STATUS                Status;
614   SERIAL_DEV                *SerialDevice;
615   UINT64                    Divisor;
616   SERIAL_PORT_LCR           Lcr;
617   UART_DEVICE_PATH          *Uart;
618   EFI_TPL                   Tpl;
619 
620   SerialDevice = SERIAL_DEV_FROM_THIS (This);
621 
622   //
623   // Check for default settings and fill in actual values.
624   //
625   if (BaudRate == 0) {
626     BaudRate = PcdGet64 (PcdUartDefaultBaudRate);
627   }
628 
629   if (ReceiveFifoDepth == 0) {
630     ReceiveFifoDepth = SerialDevice->ReceiveFifoDepth;
631   }
632 
633   if (Timeout == 0) {
634     Timeout = SERIAL_PORT_DEFAULT_TIMEOUT;
635   }
636 
637   if (Parity == DefaultParity) {
638     Parity = (EFI_PARITY_TYPE) PcdGet8 (PcdUartDefaultParity);
639   }
640 
641   if (DataBits == 0) {
642     DataBits = PcdGet8 (PcdUartDefaultDataBits);
643   }
644 
645   if (StopBits == DefaultStopBits) {
646     StopBits = (EFI_STOP_BITS_TYPE) PcdGet8 (PcdUartDefaultStopBits);
647   }
648 
649   if (!VerifyUartParameters (SerialDevice->ClockRate, BaudRate, DataBits, Parity, StopBits, &Divisor, &BaudRate)) {
650     return EFI_INVALID_PARAMETER;
651   }
652 
653   if ((ReceiveFifoDepth == 0) || (ReceiveFifoDepth > SerialDevice->ReceiveFifoDepth)) {
654     return EFI_INVALID_PARAMETER;
655   }
656 
657   if ((Timeout < SERIAL_PORT_MIN_TIMEOUT) || (Timeout > SERIAL_PORT_MAX_TIMEOUT)) {
658     return EFI_INVALID_PARAMETER;
659   }
660 
661   Tpl = gBS->RaiseTPL (TPL_NOTIFY);
662 
663   //
664   // Put serial port on Divisor Latch Mode
665   //
666   Lcr.Data      = READ_LCR (SerialDevice);
667   Lcr.Bits.DLab = 1;
668   WRITE_LCR (SerialDevice, Lcr.Data);
669 
670   //
671   // Write the divisor to the serial port
672   //
673   WRITE_DLL (SerialDevice, (UINT8) Divisor);
674   WRITE_DLM (SerialDevice, (UINT8) ((UINT16) Divisor >> 8));
675 
676   //
677   // Put serial port back in normal mode and set remaining attributes.
678   //
679   Lcr.Bits.DLab = 0;
680 
681   switch (Parity) {
682   case NoParity:
683     Lcr.Bits.ParEn    = 0;
684     Lcr.Bits.EvenPar  = 0;
685     Lcr.Bits.SticPar  = 0;
686     break;
687 
688   case EvenParity:
689     Lcr.Bits.ParEn    = 1;
690     Lcr.Bits.EvenPar  = 1;
691     Lcr.Bits.SticPar  = 0;
692     break;
693 
694   case OddParity:
695     Lcr.Bits.ParEn    = 1;
696     Lcr.Bits.EvenPar  = 0;
697     Lcr.Bits.SticPar  = 0;
698     break;
699 
700   case SpaceParity:
701     Lcr.Bits.ParEn    = 1;
702     Lcr.Bits.EvenPar  = 1;
703     Lcr.Bits.SticPar  = 1;
704     break;
705 
706   case MarkParity:
707     Lcr.Bits.ParEn    = 1;
708     Lcr.Bits.EvenPar  = 0;
709     Lcr.Bits.SticPar  = 1;
710     break;
711 
712   default:
713     break;
714   }
715 
716   switch (StopBits) {
717   case OneStopBit:
718     Lcr.Bits.StopB = 0;
719     break;
720 
721   case OneFiveStopBits:
722   case TwoStopBits:
723     Lcr.Bits.StopB = 1;
724     break;
725 
726   default:
727     break;
728   }
729   //
730   // DataBits
731   //
732   Lcr.Bits.SerialDB = (UINT8) ((DataBits - 5) & 0x03);
733   WRITE_LCR (SerialDevice, Lcr.Data);
734 
735   //
736   // Set the Serial I/O mode
737   //
738   This->Mode->BaudRate          = BaudRate;
739   This->Mode->ReceiveFifoDepth  = ReceiveFifoDepth;
740   This->Mode->Timeout           = Timeout;
741   This->Mode->Parity            = Parity;
742   This->Mode->DataBits          = DataBits;
743   This->Mode->StopBits          = StopBits;
744 
745   //
746   // See if Device Path Node has actually changed
747   //
748   if (SerialDevice->UartDevicePath.BaudRate == BaudRate &&
749       SerialDevice->UartDevicePath.DataBits == DataBits &&
750       SerialDevice->UartDevicePath.Parity == Parity &&
751       SerialDevice->UartDevicePath.StopBits == StopBits
752       ) {
753     gBS->RestoreTPL (Tpl);
754     return EFI_SUCCESS;
755   }
756   //
757   // Update the device path
758   //
759   SerialDevice->UartDevicePath.BaudRate = BaudRate;
760   SerialDevice->UartDevicePath.DataBits = DataBits;
761   SerialDevice->UartDevicePath.Parity   = (UINT8) Parity;
762   SerialDevice->UartDevicePath.StopBits = (UINT8) StopBits;
763 
764   Status = EFI_SUCCESS;
765   if (SerialDevice->Handle != NULL) {
766 
767     //
768     // Skip the optional Controller device path node
769     //
770     Uart = SkipControllerDevicePathNode (
771              (EFI_DEVICE_PATH_PROTOCOL *) (
772                (UINT8 *) SerialDevice->DevicePath + GetDevicePathSize (SerialDevice->ParentDevicePath) - END_DEVICE_PATH_LENGTH
773                ),
774              NULL,
775              NULL
776              );
777     CopyMem (Uart, &SerialDevice->UartDevicePath, sizeof (UART_DEVICE_PATH));
778     Status = gBS->ReinstallProtocolInterface (
779                     SerialDevice->Handle,
780                     &gEfiDevicePathProtocolGuid,
781                     SerialDevice->DevicePath,
782                     SerialDevice->DevicePath
783                     );
784   }
785 
786   gBS->RestoreTPL (Tpl);
787 
788   return Status;
789 }
790 
791 /**
792   Set Control Bits.
793 
794   @param This              Pointer to EFI_SERIAL_IO_PROTOCOL
795   @param Control           Control bits that can be settable
796 
797   @retval EFI_SUCCESS       New Control bits were set successfully
798   @retval EFI_UNSUPPORTED   The Control bits wanted to set are not supported
799 
800 **/
801 EFI_STATUS
802 EFIAPI
SerialSetControl(IN EFI_SERIAL_IO_PROTOCOL * This,IN UINT32 Control)803 SerialSetControl (
804   IN EFI_SERIAL_IO_PROTOCOL  *This,
805   IN UINT32                  Control
806   )
807 {
808   SERIAL_DEV                    *SerialDevice;
809   SERIAL_PORT_MCR               Mcr;
810   EFI_TPL                       Tpl;
811   UART_FLOW_CONTROL_DEVICE_PATH *FlowControl;
812   EFI_STATUS                    Status;
813 
814   //
815   // The control bits that can be set are :
816   //     EFI_SERIAL_DATA_TERMINAL_READY: 0x0001  // WO
817   //     EFI_SERIAL_REQUEST_TO_SEND: 0x0002  // WO
818   //     EFI_SERIAL_HARDWARE_LOOPBACK_ENABLE: 0x1000  // RW
819   //     EFI_SERIAL_SOFTWARE_LOOPBACK_ENABLE: 0x2000  // RW
820   //     EFI_SERIAL_HARDWARE_FLOW_CONTROL_ENABLE: 0x4000 // RW
821   //
822   SerialDevice = SERIAL_DEV_FROM_THIS (This);
823 
824   //
825   // first determine the parameter is invalid
826   //
827   if ((Control & (~(EFI_SERIAL_REQUEST_TO_SEND | EFI_SERIAL_DATA_TERMINAL_READY |
828                     EFI_SERIAL_HARDWARE_LOOPBACK_ENABLE | EFI_SERIAL_SOFTWARE_LOOPBACK_ENABLE |
829                     EFI_SERIAL_HARDWARE_FLOW_CONTROL_ENABLE))) != 0) {
830     return EFI_UNSUPPORTED;
831   }
832 
833   Tpl = gBS->RaiseTPL (TPL_NOTIFY);
834 
835   Mcr.Data = READ_MCR (SerialDevice);
836   Mcr.Bits.DtrC = 0;
837   Mcr.Bits.Rts = 0;
838   Mcr.Bits.Lme = 0;
839   SerialDevice->SoftwareLoopbackEnable = FALSE;
840   SerialDevice->HardwareFlowControl = FALSE;
841 
842   if ((Control & EFI_SERIAL_DATA_TERMINAL_READY) == EFI_SERIAL_DATA_TERMINAL_READY) {
843     Mcr.Bits.DtrC = 1;
844   }
845 
846   if ((Control & EFI_SERIAL_REQUEST_TO_SEND) == EFI_SERIAL_REQUEST_TO_SEND) {
847     Mcr.Bits.Rts = 1;
848   }
849 
850   if ((Control & EFI_SERIAL_HARDWARE_LOOPBACK_ENABLE) == EFI_SERIAL_HARDWARE_LOOPBACK_ENABLE) {
851     Mcr.Bits.Lme = 1;
852   }
853 
854   if ((Control & EFI_SERIAL_HARDWARE_FLOW_CONTROL_ENABLE) == EFI_SERIAL_HARDWARE_FLOW_CONTROL_ENABLE) {
855     SerialDevice->HardwareFlowControl = TRUE;
856   }
857 
858   WRITE_MCR (SerialDevice, Mcr.Data);
859 
860   if ((Control & EFI_SERIAL_SOFTWARE_LOOPBACK_ENABLE) == EFI_SERIAL_SOFTWARE_LOOPBACK_ENABLE) {
861     SerialDevice->SoftwareLoopbackEnable = TRUE;
862   }
863 
864   Status = EFI_SUCCESS;
865   if (SerialDevice->Handle != NULL) {
866     FlowControl = (UART_FLOW_CONTROL_DEVICE_PATH *) (
867                     (UINTN) SerialDevice->DevicePath
868                     + GetDevicePathSize (SerialDevice->ParentDevicePath)
869                     - END_DEVICE_PATH_LENGTH
870                     + sizeof (UART_DEVICE_PATH)
871                     );
872     if (IsUartFlowControlDevicePathNode (FlowControl) &&
873         ((BOOLEAN) (ReadUnaligned32 (&FlowControl->FlowControlMap) == UART_FLOW_CONTROL_HARDWARE) != SerialDevice->HardwareFlowControl)) {
874       //
875       // Flow Control setting is changed, need to reinstall device path protocol
876       //
877       WriteUnaligned32 (&FlowControl->FlowControlMap, SerialDevice->HardwareFlowControl ? UART_FLOW_CONTROL_HARDWARE : 0);
878       Status = gBS->ReinstallProtocolInterface (
879                       SerialDevice->Handle,
880                       &gEfiDevicePathProtocolGuid,
881                       SerialDevice->DevicePath,
882                       SerialDevice->DevicePath
883                       );
884     }
885   }
886 
887   gBS->RestoreTPL (Tpl);
888 
889   return Status;
890 }
891 
892 /**
893   Get ControlBits.
894 
895   @param This          Pointer to EFI_SERIAL_IO_PROTOCOL
896   @param Control       Control signals of the serial device
897 
898   @retval EFI_SUCCESS   Get Control signals successfully
899 
900 **/
901 EFI_STATUS
902 EFIAPI
SerialGetControl(IN EFI_SERIAL_IO_PROTOCOL * This,OUT UINT32 * Control)903 SerialGetControl (
904   IN EFI_SERIAL_IO_PROTOCOL  *This,
905   OUT UINT32                 *Control
906   )
907 {
908   SERIAL_DEV      *SerialDevice;
909   SERIAL_PORT_MSR Msr;
910   SERIAL_PORT_MCR Mcr;
911   EFI_TPL         Tpl;
912 
913   Tpl           = gBS->RaiseTPL (TPL_NOTIFY);
914 
915   SerialDevice  = SERIAL_DEV_FROM_THIS (This);
916 
917   *Control      = 0;
918 
919   //
920   // Read the Modem Status Register
921   //
922   Msr.Data = READ_MSR (SerialDevice);
923 
924   if (Msr.Bits.Cts == 1) {
925     *Control |= EFI_SERIAL_CLEAR_TO_SEND;
926   }
927 
928   if (Msr.Bits.Dsr == 1) {
929     *Control |= EFI_SERIAL_DATA_SET_READY;
930   }
931 
932   if (Msr.Bits.Ri == 1) {
933     *Control |= EFI_SERIAL_RING_INDICATE;
934   }
935 
936   if (Msr.Bits.Dcd == 1) {
937     *Control |= EFI_SERIAL_CARRIER_DETECT;
938   }
939   //
940   // Read the Modem Control Register
941   //
942   Mcr.Data = READ_MCR (SerialDevice);
943 
944   if (Mcr.Bits.DtrC == 1) {
945     *Control |= EFI_SERIAL_DATA_TERMINAL_READY;
946   }
947 
948   if (Mcr.Bits.Rts == 1) {
949     *Control |= EFI_SERIAL_REQUEST_TO_SEND;
950   }
951 
952   if (Mcr.Bits.Lme == 1) {
953     *Control |= EFI_SERIAL_HARDWARE_LOOPBACK_ENABLE;
954   }
955 
956   if (SerialDevice->HardwareFlowControl) {
957     *Control |= EFI_SERIAL_HARDWARE_FLOW_CONTROL_ENABLE;
958   }
959   //
960   // Update FIFO status
961   //
962   SerialReceiveTransmit (SerialDevice);
963 
964   //
965   // See if the Transmit FIFO is empty
966   //
967   if (SerialFifoEmpty (&SerialDevice->Transmit)) {
968     *Control |= EFI_SERIAL_OUTPUT_BUFFER_EMPTY;
969   }
970 
971   //
972   // See if the Receive FIFO is empty.
973   //
974   if (SerialFifoEmpty (&SerialDevice->Receive)) {
975     *Control |= EFI_SERIAL_INPUT_BUFFER_EMPTY;
976   }
977 
978   if (SerialDevice->SoftwareLoopbackEnable) {
979     *Control |= EFI_SERIAL_SOFTWARE_LOOPBACK_ENABLE;
980   }
981 
982   gBS->RestoreTPL (Tpl);
983 
984   return EFI_SUCCESS;
985 }
986 
987 /**
988   Write the specified number of bytes to serial device.
989 
990   @param This               Pointer to EFI_SERIAL_IO_PROTOCOL
991   @param  BufferSize         On input the size of Buffer, on output the amount of
992                        data actually written
993   @param  Buffer             The buffer of data to write
994 
995   @retval EFI_SUCCESS        The data were written successfully
996   @retval EFI_DEVICE_ERROR   The device reported an error
997   @retval EFI_TIMEOUT        The write operation was stopped due to timeout
998 
999 **/
1000 EFI_STATUS
1001 EFIAPI
SerialWrite(IN EFI_SERIAL_IO_PROTOCOL * This,IN OUT UINTN * BufferSize,IN VOID * Buffer)1002 SerialWrite (
1003   IN EFI_SERIAL_IO_PROTOCOL  *This,
1004   IN OUT UINTN               *BufferSize,
1005   IN VOID                    *Buffer
1006   )
1007 {
1008   SERIAL_DEV  *SerialDevice;
1009   UINT8       *CharBuffer;
1010   UINT32      Index;
1011   UINTN       Elapsed;
1012   UINTN       ActualWrite;
1013   EFI_TPL     Tpl;
1014   UINTN       Timeout;
1015   UINTN       BitsPerCharacter;
1016 
1017   SerialDevice  = SERIAL_DEV_FROM_THIS (This);
1018   Elapsed       = 0;
1019   ActualWrite   = 0;
1020 
1021   if (*BufferSize == 0) {
1022     return EFI_SUCCESS;
1023   }
1024 
1025   if (Buffer == NULL) {
1026     REPORT_STATUS_CODE_WITH_DEVICE_PATH (
1027       EFI_ERROR_CODE,
1028       EFI_P_EC_OUTPUT_ERROR | EFI_PERIPHERAL_SERIAL_PORT,
1029       SerialDevice->DevicePath
1030       );
1031 
1032     return EFI_DEVICE_ERROR;
1033   }
1034 
1035   Tpl         = gBS->RaiseTPL (TPL_NOTIFY);
1036 
1037   CharBuffer  = (UINT8 *) Buffer;
1038 
1039   //
1040   // Compute the number of bits in a single character.  This is a start bit,
1041   // followed by the number of data bits, followed by the number of stop bits.
1042   // The number of stop bits is specified by an enumeration that includes
1043   // support for 1.5 stop bits.  Treat 1.5 stop bits as 2 stop bits.
1044   //
1045   BitsPerCharacter =
1046     1 +
1047     This->Mode->DataBits +
1048     ((This->Mode->StopBits == TwoStopBits) ? 2 : This->Mode->StopBits);
1049 
1050   //
1051   // Compute the timeout in microseconds to wait for a single byte to be
1052   // transmitted.  The Mode structure contans a Timeout field that is the
1053   // maximum time to transmit or receive a character.  However, many UARTs
1054   // have a FIFO for transmits, so the time required to add one new character
1055   // to the transmit FIFO may be the time required to flush a full FIFO.  If
1056   // the Timeout in the Mode structure is smaller than the time required to
1057   // flush a full FIFO at the current baud rate, then use a timeout value that
1058   // is required to flush a full transmit FIFO.
1059   //
1060   Timeout = MAX (
1061               This->Mode->Timeout,
1062               (UINTN)DivU64x64Remainder (
1063                 BitsPerCharacter * (SerialDevice->TransmitFifoDepth + 1) * 1000000,
1064                 This->Mode->BaudRate,
1065                 NULL
1066                 )
1067               );
1068 
1069   for (Index = 0; Index < *BufferSize; Index++) {
1070     SerialFifoAdd (&SerialDevice->Transmit, CharBuffer[Index]);
1071 
1072     while (SerialReceiveTransmit (SerialDevice) != EFI_SUCCESS || !SerialFifoEmpty (&SerialDevice->Transmit)) {
1073       //
1074       //  Unsuccessful write so check if timeout has expired, if not,
1075       //  stall for a bit, increment time elapsed, and try again
1076       //
1077       if (Elapsed >= Timeout) {
1078         *BufferSize = ActualWrite;
1079         gBS->RestoreTPL (Tpl);
1080         return EFI_TIMEOUT;
1081       }
1082 
1083       gBS->Stall (TIMEOUT_STALL_INTERVAL);
1084 
1085       Elapsed += TIMEOUT_STALL_INTERVAL;
1086     }
1087 
1088     ActualWrite++;
1089     //
1090     //  Successful write so reset timeout
1091     //
1092     Elapsed = 0;
1093   }
1094 
1095   gBS->RestoreTPL (Tpl);
1096 
1097   return EFI_SUCCESS;
1098 }
1099 
1100 /**
1101   Read the specified number of bytes from serial device.
1102 
1103   @param This               Pointer to EFI_SERIAL_IO_PROTOCOL
1104   @param BufferSize         On input the size of Buffer, on output the amount of
1105                             data returned in buffer
1106   @param Buffer             The buffer to return the data into
1107 
1108   @retval EFI_SUCCESS        The data were read successfully
1109   @retval EFI_DEVICE_ERROR   The device reported an error
1110   @retval EFI_TIMEOUT        The read operation was stopped due to timeout
1111 
1112 **/
1113 EFI_STATUS
1114 EFIAPI
SerialRead(IN EFI_SERIAL_IO_PROTOCOL * This,IN OUT UINTN * BufferSize,OUT VOID * Buffer)1115 SerialRead (
1116   IN EFI_SERIAL_IO_PROTOCOL  *This,
1117   IN OUT UINTN               *BufferSize,
1118   OUT VOID                   *Buffer
1119   )
1120 {
1121   SERIAL_DEV  *SerialDevice;
1122   UINT32      Index;
1123   UINT8       *CharBuffer;
1124   UINTN       Elapsed;
1125   EFI_STATUS  Status;
1126   EFI_TPL     Tpl;
1127 
1128   SerialDevice  = SERIAL_DEV_FROM_THIS (This);
1129   Elapsed       = 0;
1130 
1131   if (*BufferSize == 0) {
1132     return EFI_SUCCESS;
1133   }
1134 
1135   if (Buffer == NULL) {
1136     return EFI_DEVICE_ERROR;
1137   }
1138 
1139   Tpl     = gBS->RaiseTPL (TPL_NOTIFY);
1140 
1141   Status  = SerialReceiveTransmit (SerialDevice);
1142 
1143   if (EFI_ERROR (Status)) {
1144     *BufferSize = 0;
1145 
1146     REPORT_STATUS_CODE_WITH_DEVICE_PATH (
1147       EFI_ERROR_CODE,
1148       EFI_P_EC_INPUT_ERROR | EFI_PERIPHERAL_SERIAL_PORT,
1149       SerialDevice->DevicePath
1150       );
1151 
1152     gBS->RestoreTPL (Tpl);
1153 
1154     return EFI_DEVICE_ERROR;
1155   }
1156 
1157   CharBuffer = (UINT8 *) Buffer;
1158   for (Index = 0; Index < *BufferSize; Index++) {
1159     while (SerialFifoRemove (&SerialDevice->Receive, &(CharBuffer[Index])) != EFI_SUCCESS) {
1160       //
1161       //  Unsuccessful read so check if timeout has expired, if not,
1162       //  stall for a bit, increment time elapsed, and try again
1163       //  Need this time out to get conspliter to work.
1164       //
1165       if (Elapsed >= This->Mode->Timeout) {
1166         *BufferSize = Index;
1167         gBS->RestoreTPL (Tpl);
1168         return EFI_TIMEOUT;
1169       }
1170 
1171       gBS->Stall (TIMEOUT_STALL_INTERVAL);
1172       Elapsed += TIMEOUT_STALL_INTERVAL;
1173 
1174       Status = SerialReceiveTransmit (SerialDevice);
1175       if (Status == EFI_DEVICE_ERROR) {
1176         *BufferSize = Index;
1177         gBS->RestoreTPL (Tpl);
1178         return EFI_DEVICE_ERROR;
1179       }
1180     }
1181     //
1182     //  Successful read so reset timeout
1183     //
1184     Elapsed = 0;
1185   }
1186 
1187   SerialReceiveTransmit (SerialDevice);
1188 
1189   gBS->RestoreTPL (Tpl);
1190 
1191   return EFI_SUCCESS;
1192 }
1193 
1194 /**
1195   Use scratchpad register to test if this serial port is present.
1196 
1197   @param SerialDevice   Pointer to serial device structure
1198 
1199   @return if this serial port is present
1200 **/
1201 BOOLEAN
SerialPresent(IN SERIAL_DEV * SerialDevice)1202 SerialPresent (
1203   IN SERIAL_DEV *SerialDevice
1204   )
1205 
1206 {
1207   UINT8   Temp;
1208   BOOLEAN Status;
1209 
1210   Status = TRUE;
1211 
1212   //
1213   // Save SCR reg
1214   //
1215   Temp = READ_SCR (SerialDevice);
1216   WRITE_SCR (SerialDevice, 0xAA);
1217 
1218   if (READ_SCR (SerialDevice) != 0xAA) {
1219     Status = FALSE;
1220   }
1221 
1222   WRITE_SCR (SerialDevice, 0x55);
1223 
1224   if (READ_SCR (SerialDevice) != 0x55) {
1225     Status = FALSE;
1226   }
1227   //
1228   // Restore SCR
1229   //
1230   WRITE_SCR (SerialDevice, Temp);
1231   return Status;
1232 }
1233 
1234 /**
1235   Read serial port.
1236 
1237   @param SerialDev     Pointer to serial device
1238   @param Offset        Offset in register group
1239 
1240   @return Data read from serial port
1241 
1242 **/
1243 UINT8
SerialReadRegister(IN SERIAL_DEV * SerialDev,IN UINT32 Offset)1244 SerialReadRegister (
1245   IN SERIAL_DEV                            *SerialDev,
1246   IN UINT32                                Offset
1247   )
1248 {
1249   UINT8                                    Data;
1250   EFI_STATUS                               Status;
1251 
1252   if (SerialDev->PciDeviceInfo == NULL) {
1253     return IoRead8 ((UINTN) SerialDev->BaseAddress + Offset * SerialDev->RegisterStride);
1254   } else {
1255     if (SerialDev->MmioAccess) {
1256       Status = SerialDev->PciDeviceInfo->PciIo->Mem.Read (SerialDev->PciDeviceInfo->PciIo, EfiPciIoWidthUint8, EFI_PCI_IO_PASS_THROUGH_BAR,
1257                                                           SerialDev->BaseAddress + Offset * SerialDev->RegisterStride, 1, &Data);
1258     } else {
1259       Status = SerialDev->PciDeviceInfo->PciIo->Io.Read (SerialDev->PciDeviceInfo->PciIo, EfiPciIoWidthUint8, EFI_PCI_IO_PASS_THROUGH_BAR,
1260                                                          SerialDev->BaseAddress + Offset * SerialDev->RegisterStride, 1, &Data);
1261     }
1262     ASSERT_EFI_ERROR (Status);
1263     return Data;
1264   }
1265 }
1266 
1267 /**
1268   Write serial port.
1269 
1270   @param  SerialDev     Pointer to serial device
1271   @param  Offset        Offset in register group
1272   @param  Data          data which is to be written to some serial port register
1273 **/
1274 VOID
SerialWriteRegister(IN SERIAL_DEV * SerialDev,IN UINT32 Offset,IN UINT8 Data)1275 SerialWriteRegister (
1276   IN SERIAL_DEV                            *SerialDev,
1277   IN UINT32                                Offset,
1278   IN UINT8                                 Data
1279   )
1280 {
1281   EFI_STATUS                               Status;
1282 
1283   if (SerialDev->PciDeviceInfo == NULL) {
1284     IoWrite8 ((UINTN) SerialDev->BaseAddress + Offset * SerialDev->RegisterStride, Data);
1285   } else {
1286     if (SerialDev->MmioAccess) {
1287       Status = SerialDev->PciDeviceInfo->PciIo->Mem.Write (SerialDev->PciDeviceInfo->PciIo, EfiPciIoWidthUint8, EFI_PCI_IO_PASS_THROUGH_BAR,
1288                                                            SerialDev->BaseAddress + Offset * SerialDev->RegisterStride, 1, &Data);
1289     } else {
1290       Status = SerialDev->PciDeviceInfo->PciIo->Io.Write (SerialDev->PciDeviceInfo->PciIo, EfiPciIoWidthUint8, EFI_PCI_IO_PASS_THROUGH_BAR,
1291                                                           SerialDev->BaseAddress + Offset * SerialDev->RegisterStride, 1, &Data);
1292     }
1293     ASSERT_EFI_ERROR (Status);
1294   }
1295 }
1296