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